Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 26c8d0ca21 | |||
| c097e122b9 | |||
| af6f65fb26 | |||
| fc8b40f2f6 | |||
| 57a8c61501 | |||
| 63cecc4173 | |||
| 1431807a0b | |||
| 773d3afaa3 | |||
| c7a77ae1c6 | |||
| aaf8a0643e | |||
| 1c12657f9a | |||
| 099a0f5f54 | |||
| 83c434e68d | |||
| ed24da4b16 | |||
| fad0e70c63 | |||
| 9f24b53dcc | |||
| c1bcd9cc3e | |||
| 3fffbf9327 | |||
| 083aefc732 | |||
| b29e60c448 | |||
| 5c3340bd47 | |||
| 80aa545337 | |||
| 8f9d1ad8d5 | |||
| 9f6dd025ba | |||
| 2718960316 | |||
| 4910a76d45 | |||
| 55a6f6dc72 | |||
| c6952addf7 | |||
| 76729ed4be | |||
| 033564cca9 | |||
| e5a0cfa161 | |||
| d0d49b9ca3 | |||
| a87070a271 | |||
| 2e5c5052fd | |||
| 6650190891 | |||
| 57b60432ba | |||
| 888e1faf95 | |||
| 1ad78c5791 | |||
| 88e665fd0b | |||
| 106526e1bc | |||
| a84a283d18 | |||
| 363fb95bc3 | |||
| 4b4f51148a | |||
| 6fc8f36490 | |||
| 0c36fca004 | |||
| b7ca2177d7 | |||
| 0dfa4f6fcc | |||
| 61441c3493 | |||
| 8e16b2d3e6 | |||
| 64bcb8cfbb | |||
| 975c800dd8 | |||
| 0a9428d0a7 | |||
| 7dd7d98bbf | |||
| a28f85b076 | |||
| f10122d535 | |||
| 3cc19132da | |||
| fb82820a38 | |||
| e347d3e813 | |||
| 0a1082aec2 | |||
| 1373c3a8c5 | |||
| ecb87ac88d | |||
| ee0f22fd3b | |||
| 539f2fef03 | |||
| 18016ad2bb | |||
| 72e26dec86 | |||
| 62659b2a80 | |||
| 37ff4d4c77 | |||
| 24b6b81cb0 | |||
| 321e828d03 | |||
| d0b67a8654 | |||
| 7887d7f24d | |||
| ac6b47d164 | |||
| 894241c847 | |||
| e835bc1285 | |||
| 66860af3dd | |||
| abb9d5f189 | |||
| 6ae3ab0036 | |||
| cbfe1101b3 | |||
| 12abf408ff | |||
| db45bd5e82 | |||
| 84b19a3da1 | |||
| d832da7f40 | |||
| 76dbe7a8d0 | |||
| 8bd0d58e1c | |||
| 783e778f38 | |||
| 9854260bca | |||
| f358009a93 | |||
| 9e730f0bb8 | |||
| 8762ec1935 | |||
| a1251bc40d | |||
| fc7f27b9d2 | |||
| 8d25cb90cd | |||
| 39a11819f3 | |||
| a15a8bc822 | |||
| c9e75fb23a | |||
| 4602b8e89d | |||
| b684fd5c4a | |||
| 05a41ee9ec | |||
| fcc31545bc | |||
| 6a754dc767 | |||
| 7ca1ea16ff | |||
| 9ef5e7708a | |||
| da84dcae54 | |||
| 61ea610cd0 | |||
| e59be01072 | |||
| 03181c8c27 | |||
| 48cb3a7600 | |||
| db97e56217 | |||
| c8d0b0b9d4 | |||
| 5ea494425f | |||
| a79e10b665 | |||
| b39a5ac229 | |||
| 69c338263a | |||
| e7036202bc | |||
| 8c2b1fde58 | |||
| 0f832ddc06 | |||
| 0f825a7e26 | |||
| af69eda63e | |||
| a2f629251f | |||
| 460d38f15e | |||
| 79752b6e63 | |||
| 3b96d1e2e4 | |||
| 471b4b92bd | |||
| a48d7e7749 | |||
| 1bf4ca7c42 | |||
| 088645b090 | |||
| 17994e3bca | |||
| 6ff4627d5d | |||
| c73bd0a5cc | |||
| 93ca3933d1 | |||
| e104dd3cc3 | |||
| 922802c44e | |||
| f5dbe7f8d8 | |||
| 167fbbe195 | |||
| 765fecf010 | |||
| 701b68879c | |||
| d9fabbcc5b | |||
| c83db9e4e1 | |||
| 8d57d7af23 | |||
| ccf03677a3 | |||
| 882ef0b8c0 | |||
| 9cd4933ec1 | |||
| 6256c1c242 | |||
| 1d9bc9b7a0 | |||
| adeb970da2 | |||
| 3d1fb1dd75 | |||
| 2c628ea9d9 | |||
| d66e5794d1 | |||
| 78f9bb6c71 | |||
| 51d82d1d24 | |||
| f9b2e05c75 | |||
| d05a7144cd | |||
| 080508a186 | |||
| 95a08c065a | |||
| 553168c706 | |||
| 870a0705fe | |||
| 5c288b929a | |||
| cb17ce9300 | |||
| 625015e0c9 | |||
| 19ff12dd76 | |||
| 145b4ea519 | |||
| 7b35a77b1a | |||
| 34c125747d | |||
| d49fd4e89a | |||
| d9ed09d47b | |||
| 8da209b169 | |||
| ba00e30a3a | |||
| 4097d0ca79 | |||
| cfbb5e82db | |||
| 709947a315 | |||
| 71c57db099 |
@@ -24,6 +24,13 @@ USE_AMALGAMATION = 1
|
||||
USE_FULLWARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
|
||||
# has no effect if (any) optimizations are enabled.
|
||||
#
|
||||
!IFNDEF USE_RUNTIME_CHECKS
|
||||
USE_RUNTIME_CHECKS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use "stdcall" calling convention for the core library
|
||||
# and shell executable.
|
||||
#
|
||||
@@ -835,6 +842,10 @@ RCC = $(RCC) -D_DEBUG
|
||||
!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
|
||||
TCC = $(TCC) -Od
|
||||
BCC = $(BCC) -Od
|
||||
!IF $(USE_RUNTIME_CHECKS)!=0
|
||||
TCC = $(TCC) -RTC1
|
||||
BCC = $(BCC) -RTC1
|
||||
!ENDIF
|
||||
!ELSEIF $(OPTIMIZATIONS)>=3
|
||||
TCC = $(TCC) -Ox
|
||||
BCC = $(BCC) -Ox
|
||||
|
||||
@@ -456,12 +456,12 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
|
||||
#
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
!ELSE
|
||||
!IFNDEF PLATFORM
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.14.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.15.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.14.0'
|
||||
PACKAGE_STRING='sqlite 3.14.0'
|
||||
PACKAGE_VERSION='3.15.0'
|
||||
PACKAGE_STRING='sqlite 3.15.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1463,7 +1463,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.14.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.15.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1528,7 +1528,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.14.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.15.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1652,7 +1652,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.14.0
|
||||
sqlite configure 3.15.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2071,7 +2071,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.14.0, which was
|
||||
It was created by sqlite $as_me 3.15.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12151,7 +12151,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.14.0, which was
|
||||
This file was extended by sqlite $as_me 3.15.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12217,7 +12217,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.14.0
|
||||
sqlite config.status 3.15.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -737,6 +737,7 @@ void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
|
||||
|
||||
void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
|
||||
void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
|
||||
Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*);
|
||||
void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
|
||||
void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
|
||||
|
||||
|
||||
+185
-45
@@ -189,7 +189,7 @@ static int fts5HighlightCb(
|
||||
if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
|
||||
p->iOff = iEndOff;
|
||||
if( iPos<p->iter.iEnd ){
|
||||
if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,115 @@ static void fts5HighlightFunction(
|
||||
** End of highlight() implementation.
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
** Context object passed to the fts5SentenceFinderCb() function.
|
||||
*/
|
||||
typedef struct Fts5SFinder Fts5SFinder;
|
||||
struct Fts5SFinder {
|
||||
int iPos; /* Current token position */
|
||||
int nFirstAlloc; /* Allocated size of aFirst[] */
|
||||
int nFirst; /* Number of entries in aFirst[] */
|
||||
int *aFirst; /* Array of first token in each sentence */
|
||||
const char *zDoc; /* Document being tokenized */
|
||||
};
|
||||
|
||||
/*
|
||||
** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if
|
||||
** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an
|
||||
** error occurs.
|
||||
*/
|
||||
static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
|
||||
if( p->nFirstAlloc==p->nFirst ){
|
||||
int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
|
||||
int *aNew;
|
||||
|
||||
aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
p->aFirst = aNew;
|
||||
p->nFirstAlloc = nNew;
|
||||
}
|
||||
p->aFirst[p->nFirst++] = iAdd;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is an xTokenize() callback used by the auxiliary snippet()
|
||||
** function. Its job is to identify tokens that are the first in a sentence.
|
||||
** For each such token, an entry is added to the SFinder.aFirst[] array.
|
||||
*/
|
||||
static int fts5SentenceFinderCb(
|
||||
void *pContext, /* Pointer to HighlightContext object */
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStartOff, /* Start offset of token */
|
||||
int iEndOff /* End offset of token */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
|
||||
Fts5SFinder *p = (Fts5SFinder*)pContext;
|
||||
if( p->iPos>0 ){
|
||||
int i;
|
||||
char c = 0;
|
||||
for(i=iStartOff-1; i>=0; i--){
|
||||
c = p->zDoc[i];
|
||||
if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break;
|
||||
}
|
||||
if( i!=iStartOff-1 && (c=='.' || c==':') ){
|
||||
rc = fts5SentenceFinderAdd(p, p->iPos);
|
||||
}
|
||||
}else{
|
||||
rc = fts5SentenceFinderAdd(p, 0);
|
||||
}
|
||||
p->iPos++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5SnippetScore(
|
||||
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
int nDocsize, /* Size of column in tokens */
|
||||
unsigned char *aSeen, /* Array with one element per query phrase */
|
||||
int iCol, /* Column to score */
|
||||
int iPos, /* Starting offset to score */
|
||||
int nToken, /* Max tokens per snippet */
|
||||
int *pnScore, /* OUT: Score */
|
||||
int *piPos /* OUT: Adjusted offset */
|
||||
){
|
||||
int rc;
|
||||
int i;
|
||||
int ip = 0;
|
||||
int ic = 0;
|
||||
int iOff = 0;
|
||||
int iFirst = -1;
|
||||
int nInst;
|
||||
int nScore = 0;
|
||||
int iLast = 0;
|
||||
|
||||
rc = pApi->xInstCount(pFts, &nInst);
|
||||
for(i=0; i<nInst && rc==SQLITE_OK; i++){
|
||||
rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
|
||||
if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
|
||||
nScore += (aSeen[ip] ? 1 : 1000);
|
||||
aSeen[ip] = 1;
|
||||
if( iFirst<0 ) iFirst = iOff;
|
||||
iLast = iOff + pApi->xPhraseSize(pFts, ip);
|
||||
}
|
||||
}
|
||||
|
||||
*pnScore = nScore;
|
||||
if( piPos ){
|
||||
int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
|
||||
if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
|
||||
if( iAdj<0 ) iAdj = 0;
|
||||
*piPos = iAdj;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of snippet() function.
|
||||
*/
|
||||
@@ -267,9 +376,10 @@ static void fts5SnippetFunction(
|
||||
unsigned char *aSeen; /* Array of "seen instance" flags */
|
||||
int iBestCol; /* Column containing best snippet */
|
||||
int iBestStart = 0; /* First token of best snippet */
|
||||
int iBestLast; /* Last token of best snippet */
|
||||
int nBestScore = 0; /* Score of best snippet */
|
||||
int nColSize = 0; /* Total size of iBestCol in tokens */
|
||||
Fts5SFinder sFinder; /* Used to find the beginnings of sentences */
|
||||
int nCol;
|
||||
|
||||
if( nVal!=5 ){
|
||||
const char *zErr = "wrong number of arguments to function snippet()";
|
||||
@@ -277,13 +387,13 @@ static void fts5SnippetFunction(
|
||||
return;
|
||||
}
|
||||
|
||||
nCol = pApi->xColumnCount(pFts);
|
||||
memset(&ctx, 0, sizeof(HighlightContext));
|
||||
iCol = sqlite3_value_int(apVal[0]);
|
||||
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
|
||||
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
|
||||
zEllips = (const char*)sqlite3_value_text(apVal[3]);
|
||||
nToken = sqlite3_value_int(apVal[4]);
|
||||
iBestLast = nToken-1;
|
||||
|
||||
iBestCol = (iCol>=0 ? iCol : 0);
|
||||
nPhrase = pApi->xPhraseCount(pFts);
|
||||
@@ -291,65 +401,95 @@ static void fts5SnippetFunction(
|
||||
if( aSeen==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xInstCount(pFts, &nInst);
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<nInst; i++){
|
||||
int ip, iSnippetCol, iStart;
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = pApi->xInst(pFts, i, &ip, &iSnippetCol, &iStart);
|
||||
if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){
|
||||
int nScore = 1000;
|
||||
int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip);
|
||||
int j;
|
||||
aSeen[ip] = 1;
|
||||
|
||||
for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
|
||||
int ic; int io; int iFinal;
|
||||
rc = pApi->xInst(pFts, j, &ip, &ic, &io);
|
||||
iFinal = io + pApi->xPhraseSize(pFts, ip) - 1;
|
||||
if( rc==SQLITE_OK && ic==iSnippetCol && iLast<iStart+nToken ){
|
||||
nScore += aSeen[ip] ? 1000 : 1;
|
||||
aSeen[ip] = 1;
|
||||
if( iFinal>iLast ) iLast = iFinal;
|
||||
memset(&sFinder, 0, sizeof(Fts5SFinder));
|
||||
for(i=0; i<nCol; i++){
|
||||
if( iCol<0 || iCol==i ){
|
||||
int nDoc;
|
||||
int nDocsize;
|
||||
int ii;
|
||||
sFinder.iPos = 0;
|
||||
sFinder.nFirst = 0;
|
||||
rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xTokenize(pFts,
|
||||
sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
|
||||
);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xColumnSize(pFts, i, &nDocsize);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
|
||||
int ip, ic, io;
|
||||
int iAdj;
|
||||
int nScore;
|
||||
int jj;
|
||||
|
||||
rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
|
||||
if( ic!=i || rc!=SQLITE_OK ) continue;
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
|
||||
io, nToken, &nScore, &iAdj
|
||||
);
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
nBestScore = nScore;
|
||||
iBestCol = i;
|
||||
iBestStart = iAdj;
|
||||
nColSize = nDocsize;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
iBestCol = iSnippetCol;
|
||||
iBestStart = iStart;
|
||||
iBestLast = iLast;
|
||||
nBestScore = nScore;
|
||||
if( rc==SQLITE_OK && sFinder.nFirst && nDocsize>nToken ){
|
||||
for(jj=0; jj<(sFinder.nFirst-1); jj++){
|
||||
if( sFinder.aFirst[jj+1]>io ) break;
|
||||
}
|
||||
|
||||
if( sFinder.aFirst[jj]<io ){
|
||||
int nScore;
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
|
||||
sFinder.aFirst[jj], nToken, &nScore, 0
|
||||
);
|
||||
|
||||
nScore += (sFinder.aFirst[jj]==0 ? 120 : 100);
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
nBestScore = nScore;
|
||||
iBestCol = i;
|
||||
iBestStart = sFinder.aFirst[jj];
|
||||
nColSize = nDocsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
|
||||
}
|
||||
if( rc==SQLITE_OK && nColSize==0 ){
|
||||
rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
|
||||
}
|
||||
if( ctx.zIn ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
|
||||
}
|
||||
|
||||
if( (iBestStart+nToken-1)>iBestLast ){
|
||||
iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
|
||||
}
|
||||
if( iBestStart+nToken>nColSize ){
|
||||
iBestStart = nColSize - nToken;
|
||||
}
|
||||
if( iBestStart<0 ) iBestStart = 0;
|
||||
|
||||
ctx.iRangeStart = iBestStart;
|
||||
ctx.iRangeEnd = iBestStart + nToken - 1;
|
||||
|
||||
if( iBestStart>0 ){
|
||||
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
|
||||
}
|
||||
|
||||
/* Advance iterator ctx.iter so that it points to the first coalesced
|
||||
** phrase instance at or following position iBestStart. */
|
||||
while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterNext(&ctx.iter);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
}
|
||||
@@ -358,15 +498,15 @@ static void fts5SnippetFunction(
|
||||
}else{
|
||||
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
sqlite3_free(ctx.zOut);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
sqlite3_free(ctx.zOut);
|
||||
sqlite3_free(aSeen);
|
||||
sqlite3_free(sFinder.aFirst);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
+30
-1
@@ -167,6 +167,7 @@ static int fts5ExprGetToken(
|
||||
case ',': tok = FTS5_COMMA; break;
|
||||
case '+': tok = FTS5_PLUS; break;
|
||||
case '*': tok = FTS5_STAR; break;
|
||||
case '-': tok = FTS5_MINUS; break;
|
||||
case '\0': tok = FTS5_EOF; break;
|
||||
|
||||
case '"': {
|
||||
@@ -1658,7 +1659,7 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
if( rc==SQLITE_OK ){
|
||||
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
|
||||
if( pColsetOrig ){
|
||||
int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int);
|
||||
int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
|
||||
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
|
||||
if( pColset ){
|
||||
memcpy(pColset, pColsetOrig, nByte);
|
||||
@@ -1793,6 +1794,34 @@ static Fts5Colset *fts5ParseColset(
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return an Fts5Colset object specifying the inverse of
|
||||
** the colset passed as the second argument. Free the colset passed
|
||||
** as the second argument before returning.
|
||||
*/
|
||||
Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
|
||||
Fts5Colset *pRet;
|
||||
int nCol = pParse->pConfig->nCol;
|
||||
|
||||
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
|
||||
sizeof(Fts5Colset) + sizeof(int)*nCol
|
||||
);
|
||||
if( pRet ){
|
||||
int i;
|
||||
int iOld = 0;
|
||||
for(i=0; i<nCol; i++){
|
||||
if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
|
||||
pRet->aiCol[pRet->nCol++] = i;
|
||||
}else{
|
||||
iOld++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(p);
|
||||
return pRet;
|
||||
}
|
||||
|
||||
Fts5Colset *sqlite3Fts5ParseColset(
|
||||
Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
|
||||
Fts5Colset *pColset, /* Existing colset object */
|
||||
|
||||
+32
-4
@@ -702,7 +702,6 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
return pRet;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Release a reference to data record returned by an earlier call to
|
||||
** fts5DataRead().
|
||||
@@ -711,6 +710,18 @@ static void fts5DataRelease(Fts5Data *pData){
|
||||
sqlite3_free(pData);
|
||||
}
|
||||
|
||||
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->szLeaf>pRet->nn ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
static int fts5IndexPrepareStmt(
|
||||
Fts5Index *p,
|
||||
sqlite3_stmt **ppStmt,
|
||||
@@ -1519,7 +1530,7 @@ static void fts5SegIterNextPage(
|
||||
pIter->pLeaf = pIter->pNextLeaf;
|
||||
pIter->pNextLeaf = 0;
|
||||
}else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
|
||||
pIter->pLeaf = fts5DataRead(p,
|
||||
pIter->pLeaf = fts5LeafRead(p,
|
||||
FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
|
||||
);
|
||||
}else{
|
||||
@@ -2022,9 +2033,8 @@ static void fts5SegIterNext(
|
||||
if( pLeaf->nn>pLeaf->szLeaf ){
|
||||
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
|
||||
&pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
else if( pLeaf->nn>pLeaf->szLeaf ){
|
||||
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
|
||||
@@ -2269,6 +2279,11 @@ static void fts5LeafSeek(
|
||||
iTermOff += nKeep;
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the nKeep field of the next term. */
|
||||
fts5FastGetVarint32(a, iOff, nKeep);
|
||||
}
|
||||
@@ -3195,6 +3210,15 @@ static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match
|
||||
** against no columns at all).
|
||||
*/
|
||||
static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){
|
||||
UNUSED_PARAM(pSeg);
|
||||
pIter->base.nData = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** xSetOutputs callback used by detail=col when there is a column filter
|
||||
** and there are 100 or more columns. Also called as a fallback from
|
||||
@@ -3300,6 +3324,10 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
|
||||
}
|
||||
|
||||
else if( pIter->pColset->nCol==0 ){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset;
|
||||
}
|
||||
|
||||
else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_Full;
|
||||
}
|
||||
|
||||
@@ -120,10 +120,17 @@ cnearset(A) ::= colset(X) COLON nearset(Y). {
|
||||
%type colsetlist {Fts5Colset*}
|
||||
%destructor colsetlist { sqlite3_free($$); }
|
||||
|
||||
colset(A) ::= MINUS LCP colsetlist(X) RCP. {
|
||||
A = sqlite3Fts5ParseColsetInvert(pParse, X);
|
||||
}
|
||||
colset(A) ::= LCP colsetlist(X) RCP. { A = X; }
|
||||
colset(A) ::= STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
}
|
||||
colset(A) ::= MINUS STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
A = sqlite3Fts5ParseColsetInvert(pParse, A);
|
||||
}
|
||||
|
||||
colsetlist(A) ::= colsetlist(Y) STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, Y, &X); }
|
||||
@@ -131,7 +138,6 @@ colsetlist(A) ::= STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
}
|
||||
|
||||
|
||||
%type nearset {Fts5ExprNearset*}
|
||||
%type nearphrases {Fts5ExprNearset*}
|
||||
%destructor nearset { sqlite3Fts5ParseNearsetFree($$); }
|
||||
|
||||
+62
-29
@@ -72,42 +72,56 @@ foreach {tn doc res} {
|
||||
2.2 {o X o o o o o o} {o [X] o o o o o...}
|
||||
2.3 {o o X o o o o o} {o o [X] o o o o...}
|
||||
2.4 {o o o X o o o o} {o o o [X] o o o...}
|
||||
2.5 {o o o o X o o o} {...o o o [X] o o o}
|
||||
2.6 {o o o o o X o o} {...o o o o [X] o o}
|
||||
2.7 {o o o o o o X o} {...o o o o o [X] o}
|
||||
2.5 {o o o o X o o o} {o o o o [X] o o...}
|
||||
2.6 {o o o o o X o o} {o o o o o [X] o...}
|
||||
2.7 {o o o o o o X o} {o o o o o o [X]...}
|
||||
2.8 {o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
2.9 {o o o o o o o X o} {...o o o o o [X] o}
|
||||
2.10 {o o o o o o o X o o} {...o o o o [X] o o}
|
||||
2.11 {o o o o o o o X o o o} {...o o o [X] o o o}
|
||||
2.12 {o o o o o o o X o o o o} {...o o o [X] o o o...}
|
||||
|
||||
|
||||
3.1 {X o o o o o o o o} {[X] o o o o o o...}
|
||||
3.2 {o X o o o o o o o} {o [X] o o o o o...}
|
||||
3.3 {o o X o o o o o o} {o o [X] o o o o...}
|
||||
3.4 {o o o X o o o o o} {o o o [X] o o o...}
|
||||
3.5 {o o o o X o o o o} {...o o o [X] o o o...}
|
||||
3.6 {o o o o o X o o o} {...o o o [X] o o o}
|
||||
3.7 {o o o o o o X o o} {...o o o o [X] o o}
|
||||
3.8 {o o o o o o o X o} {...o o o o o [X] o}
|
||||
3.9 {o o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
3.5 {o o o o o o o X o o o o} {...o o o [X] o o o...}
|
||||
3.6 {o o o o o o o o X o o o} {...o o o [X] o o o}
|
||||
3.7 {o o o o o o o o o X o o} {...o o o o [X] o o}
|
||||
3.8 {o o o o o o o o o o X o} {...o o o o o [X] o}
|
||||
3.9 {o o o o o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
4.1 {X o o o o o X o o} {[X] o o o o o [X]...}
|
||||
4.2 {o X o o o o o X o} {...[X] o o o o o [X]...}
|
||||
4.3 {o o X o o o o o X} {...[X] o o o o o [X]}
|
||||
4.2 {o o o o o o o X o o o o o X o} {...[X] o o o o o [X]...}
|
||||
4.3 {o o o o o o o o X o o o o o X} {...[X] o o o o o [X]}
|
||||
|
||||
5.1 {X o o o o X o o o} {[X] o o o o [X] o...}
|
||||
5.2 {o X o o o o X o o} {...[X] o o o o [X] o...}
|
||||
5.3 {o o X o o o o X o} {...[X] o o o o [X] o}
|
||||
5.4 {o o o X o o o o X} {...o [X] o o o o [X]}
|
||||
5.2 {o o o o o o o X o o o o X o o} {...[X] o o o o [X] o...}
|
||||
5.3 {o o o o o o o o X o o o o X o} {...[X] o o o o [X] o}
|
||||
5.4 {o o o o o o o o o X o o o o X} {...o [X] o o o o [X]}
|
||||
|
||||
6.1 {X o o o X o o o} {[X] o o o [X] o o...}
|
||||
6.2 {o X o o o X o o o} {o [X] o o o [X] o...}
|
||||
6.3 {o o X o o o X o o} {...o [X] o o o [X] o...}
|
||||
6.4 {o o o X o o o X o} {...o [X] o o o [X] o}
|
||||
6.5 {o o o o X o o o X} {...o o [X] o o o [X]}
|
||||
6.3 {o o o o o o o X o o o X o o} {...o [X] o o o [X] o...}
|
||||
6.4 {o o o o o o o o X o o o X o} {...o [X] o o o [X] o}
|
||||
6.5 {o o o o o o o o o X o o o X} {...o o [X] o o o [X]}
|
||||
|
||||
7.1 {X o o X o o o o o} {[X] o o [X] o o o...}
|
||||
7.2 {o X o o X o o o o} {o [X] o o [X] o o...}
|
||||
7.3 {o o X o o X o o o} {...o [X] o o [X] o o...}
|
||||
7.4 {o o o X o o X o o} {...o [X] o o [X] o o}
|
||||
7.5 {o o o o X o o X o} {...o o [X] o o [X] o}
|
||||
7.6 {o o o o o X o o X} {...o o o [X] o o [X]}
|
||||
7.3 {o o o o o o o X o o X o o o} {...o [X] o o [X] o o...}
|
||||
7.4 {o o o o o o o o X o o X o o} {...o [X] o o [X] o o}
|
||||
7.5 {o o o o o o o o o X o o X o} {...o o [X] o o [X] o}
|
||||
7.6 {o o o o o o o o o o X o o X} {...o o o [X] o o [X]}
|
||||
|
||||
8.1 {o o o o o o o o o X o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{...o o [X] [X] [X] o o...}
|
||||
8.2 {o o o o o o o. o o X o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{...o o [X] o o o o...}
|
||||
8.3 {o o o o X o o o o o o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{o o o o [X] o o...}
|
||||
} {
|
||||
do_snippet_test 1.$tn $doc X $res
|
||||
}
|
||||
@@ -124,24 +138,43 @@ if {[detail_is_full]} {
|
||||
2.1 {X Y o o o o o o} {[X Y] o o o o o...}
|
||||
2.2 {o X Y o o o o o} {o [X Y] o o o o...}
|
||||
2.3 {o o X Y o o o o} {o o [X Y] o o o...}
|
||||
2.4 {o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o X Y} {...o o o o o [X Y]}
|
||||
2.4 {o o o o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
|
||||
3.1 {X Y o o o o o o o} {[X Y] o o o o o...}
|
||||
3.2 {o X Y o o o o o o} {o [X Y] o o o o...}
|
||||
3.3 {o o X Y o o o o o} {o o [X Y] o o o...}
|
||||
3.4 {o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
3.4 {o o o o o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
} {
|
||||
do_snippet_test 2.$tn $doc "X + Y" $res
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b);
|
||||
INSERT INTO x1 VALUES('xyz', '1 2 3 4 5 6 7 8 9 10 11 12 13');
|
||||
SELECT snippet(x1, 1, '[', ']', '...', 5) FROM x1('xyz');
|
||||
} {
|
||||
{1 2 3 4 5...}
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE p1 USING fts5(a, b);
|
||||
INSERT INTO p1 VALUES(
|
||||
'x a a a a a a a a a a',
|
||||
'a a a a a a a a a a a a a a a a a a a x'
|
||||
);
|
||||
}
|
||||
do_execsql_test 5.1 {
|
||||
SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x');
|
||||
} {{[x] a a a a a...}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 2016 August 10
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5colset
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $::testprefix {
|
||||
if {[detail_is_none]} continue
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, d, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('a', 'b', 'c', 'd'); -- 1
|
||||
INSERT INTO t1 VALUES('d', 'a', 'b', 'c'); -- 2
|
||||
INSERT INTO t1 VALUES('c', 'd', 'a', 'b'); -- 3
|
||||
INSERT INTO t1 VALUES('b', 'c', 'd', 'a'); -- 4
|
||||
}
|
||||
|
||||
foreach {tn q res} {
|
||||
1 "a" {1 2 3 4}
|
||||
2 "{a} : a" {1}
|
||||
3 "-{a} : a" {2 3 4}
|
||||
4 "- {a c} : a" {2 4}
|
||||
5 " - {d d c} : a" {1 2}
|
||||
6 "- {d c b a} : a" {}
|
||||
7 "-{\"a\"} : b" {1 2 3}
|
||||
8 "- c : a" {1 2 4}
|
||||
9 "-c : a" {1 2 4}
|
||||
10 "-\"c\" : a" {1 2 4}
|
||||
} {
|
||||
breakpoint
|
||||
do_execsql_test 1.$tn {
|
||||
SELECT rowid FROM t1($q)
|
||||
} $res
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ do_execsql_test 1.0 {
|
||||
}
|
||||
set mask [expr 31 << 31]
|
||||
|
||||
if 1 {
|
||||
if 0 {
|
||||
|
||||
# Test 1:
|
||||
#
|
||||
@@ -84,6 +84,8 @@ foreach {tno stmt} {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Using the same database as the 1.* tests.
|
||||
#
|
||||
# Run N-1 tests, where N is the number of bytes in the rightmost leaf page
|
||||
@@ -212,8 +214,6 @@ foreach {tn nCut} {
|
||||
# do_test 4.$tn.x { expr $nCorrupt>0 } 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set doc [string repeat "A B C " 1000]
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE x5 USING fts5(tt);
|
||||
|
||||
@@ -160,12 +160,12 @@ foreach {tn query snippet} {
|
||||
the maximum x value.
|
||||
}
|
||||
4 "rollback" {
|
||||
...[ROLLBACK]. Instead, the pending statement
|
||||
will return SQLITE_ABORT upon next access after the [ROLLBACK].
|
||||
Pending statements no longer block [ROLLBACK]. Instead, the pending
|
||||
statement will return SQLITE_ABORT upon...
|
||||
}
|
||||
5 "rOllback" {
|
||||
...[ROLLBACK]. Instead, the pending statement
|
||||
will return SQLITE_ABORT upon next access after the [ROLLBACK].
|
||||
Pending statements no longer block [ROLLBACK]. Instead, the pending
|
||||
statement will return SQLITE_ABORT upon...
|
||||
}
|
||||
6 "lang*" {
|
||||
Added support for the FTS4 [languageid] option.
|
||||
|
||||
@@ -140,6 +140,15 @@ foreach {tn init mod} {
|
||||
);
|
||||
}
|
||||
|
||||
4 {
|
||||
CREATE TABLE x1(a, b, c, PRIMARY KEY(a, b, c));
|
||||
INSERT INTO x1 VALUES('u', 'v', NULL);
|
||||
INSERT INTO x1 VALUES('x', 'y', 'z');
|
||||
INSERT INTO x1 VALUES('a', NULL, 'b');
|
||||
} {
|
||||
INSERT INTO x1 VALUES('a', 'b', 'c');
|
||||
}
|
||||
|
||||
} {
|
||||
catch { db close }
|
||||
|
||||
@@ -280,5 +289,6 @@ tablE t1 USING FTs5(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -361,6 +361,8 @@ foreach {bReopen} { 0 1 } {
|
||||
}
|
||||
} {
|
||||
|
||||
if {$tn=="vtab"} { ifcapable !fts5 break }
|
||||
|
||||
foreach {tn2 rbusql r1 r2} {
|
||||
1 {
|
||||
CREATE TABLE data0_t1(a, b, c, rbu_rowid, rbu_control);
|
||||
|
||||
@@ -156,7 +156,49 @@ foreach step {0 1} {
|
||||
trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that passing a NULL value as the second argument to
|
||||
# sqlite3rbu_vacuum() causes it to:
|
||||
#
|
||||
# * Use <database>-vacuum as the state db, and
|
||||
# * Set the state db permissions to the same as those on the db file.
|
||||
#
|
||||
db close
|
||||
if {$::tcl_platform(platform)=="unix"} {
|
||||
forcedelete test.db
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8);
|
||||
}
|
||||
db close
|
||||
|
||||
foreach {tn perm} {
|
||||
1 00755
|
||||
2 00666
|
||||
3 00644
|
||||
4 00444
|
||||
} {
|
||||
forcedelete test.db-vacuum
|
||||
|
||||
do_test 5.$tn.1 {
|
||||
file attributes test.db -permissions $perm
|
||||
sqlite3rbu_vacuum rbu test.db
|
||||
rbu step
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 5.$tn.2 { file exists test.db-vacuum } 1
|
||||
do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm
|
||||
rbu close
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+25
-7
@@ -2334,15 +2334,18 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
|
||||
** error occurs, leave an error code and message in the RBU handle.
|
||||
*/
|
||||
static void rbuOpenDatabase(sqlite3rbu *p){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( p->dbMain==0 && p->dbRbu==0 );
|
||||
assert( rbuIsVacuum(p) || p->zTarget!=0 );
|
||||
assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
|
||||
assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
|
||||
|
||||
/* Open the RBU database */
|
||||
p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
|
||||
|
||||
if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
|
||||
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
|
||||
if( p->zState==0 ){
|
||||
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
|
||||
p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
|
||||
}
|
||||
}
|
||||
|
||||
/* If using separate RBU and state databases, attach the state database to
|
||||
@@ -3477,8 +3480,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
sqlite3rbu *p;
|
||||
size_t nTarget = zTarget ? strlen(zTarget) : 0;
|
||||
size_t nRbu = strlen(zRbu);
|
||||
size_t nState = zState ? strlen(zState) : 0;
|
||||
size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;
|
||||
size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
|
||||
|
||||
p = (sqlite3rbu*)sqlite3_malloc64(nByte);
|
||||
if( p ){
|
||||
@@ -3500,8 +3502,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
memcpy(p->zRbu, zRbu, nRbu+1);
|
||||
pCsr += nRbu+1;
|
||||
if( zState ){
|
||||
p->zState = pCsr;
|
||||
memcpy(p->zState, zState, nState+1);
|
||||
p->zState = rbuMPrintf(p, "%s", zState);
|
||||
}
|
||||
rbuOpenDatabase(p);
|
||||
}
|
||||
@@ -3611,6 +3612,20 @@ static sqlite3rbu *openRbuHandle(
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return an RBU handle with all fields zeroed except for the
|
||||
** error code, which is set to SQLITE_MISUSE.
|
||||
*/
|
||||
static sqlite3rbu *rbuMisuseError(void){
|
||||
sqlite3rbu *pRet;
|
||||
pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(sqlite3rbu));
|
||||
pRet->rc = SQLITE_MISUSE;
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open and return a new RBU handle.
|
||||
*/
|
||||
@@ -3619,6 +3634,7 @@ sqlite3rbu *sqlite3rbu_open(
|
||||
const char *zRbu,
|
||||
const char *zState
|
||||
){
|
||||
if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
|
||||
/* TODO: Check that zTarget and zRbu are non-NULL */
|
||||
return openRbuHandle(zTarget, zRbu, zState);
|
||||
}
|
||||
@@ -3630,6 +3646,7 @@ sqlite3rbu *sqlite3rbu_vacuum(
|
||||
const char *zTarget,
|
||||
const char *zState
|
||||
){
|
||||
if( zTarget==0 ){ return rbuMisuseError(); }
|
||||
/* TODO: Check that both arguments are non-NULL */
|
||||
return openRbuHandle(0, zTarget, zState);
|
||||
}
|
||||
@@ -3707,6 +3724,7 @@ int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
|
||||
rbuEditErrmsg(p);
|
||||
rc = p->rc;
|
||||
*pzErrmsg = p->zErrmsg;
|
||||
sqlite3_free(p->zState);
|
||||
sqlite3_free(p);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
|
||||
+12
-6
@@ -104,7 +104,7 @@
|
||||
** may also be named data<integer>_<target>, where <integer> is any sequence
|
||||
** of zero or more numeric characters (0-9). This can be significant because
|
||||
** tables within the RBU database are always processed in order sorted by
|
||||
** name. By judicious selection of the the <integer> portion of the names
|
||||
** name. By judicious selection of the <integer> portion of the names
|
||||
** of the RBU tables the user can therefore control the order in which they
|
||||
** are processed. This can be useful, for example, to ensure that "external
|
||||
** content" FTS4 tables are updated before their underlying content tables.
|
||||
@@ -319,16 +319,22 @@ sqlite3rbu *sqlite3rbu_open(
|
||||
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
|
||||
** that it can be suspended and resumed like an RBU update.
|
||||
**
|
||||
** The second argument to this function, which may not be NULL, identifies
|
||||
** a database in which to store the state of the RBU vacuum operation if
|
||||
** it is suspended. The first time sqlite3rbu_vacuum() is called, to start
|
||||
** an RBU vacuum operation, the state database should either not exist or
|
||||
** be empty (contain no tables). If an RBU vacuum is suspended by calling
|
||||
** The second argument to this function identifies a database in which
|
||||
** to store the state of the RBU vacuum operation if it is suspended. The
|
||||
** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
|
||||
** operation, the state database should either not exist or be empty
|
||||
** (contain no tables). If an RBU vacuum is suspended by calling
|
||||
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
|
||||
** returned SQLITE_DONE, the vacuum state is stored in the state database.
|
||||
** The vacuum can be resumed by calling this function to open a new RBU
|
||||
** handle specifying the same target and state databases.
|
||||
**
|
||||
** If the second argument passed to this function is NULL, then the
|
||||
** name of the state database is "<database>-vacuum", where <database>
|
||||
** is the name of the target database file. In this case, on UNIX, if the
|
||||
** state database is not already present in the file-system, it is created
|
||||
** with the same permissions as the target db is made.
|
||||
**
|
||||
** This function does not delete the state database after an RBU vacuum
|
||||
** is completed, even if it created it. However, if the call to
|
||||
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
|
||||
|
||||
+3
-3
@@ -240,13 +240,13 @@ static int SQLITE_TCLAPI test_sqlite3rbu_vacuum(
|
||||
const char *zTarget;
|
||||
const char *zStateDb = 0;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB STATE-DB");
|
||||
if( objc!=3 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zCmd = Tcl_GetString(objv[1]);
|
||||
zTarget = Tcl_GetString(objv[2]);
|
||||
zStateDb = Tcl_GetString(objv[3]);
|
||||
if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);
|
||||
|
||||
pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
|
||||
|
||||
+1
-1
@@ -1542,7 +1542,7 @@ static int rtreeFilter(
|
||||
if( idxNum==1 ){
|
||||
/* Special case - lookup by rowid. */
|
||||
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
||||
RtreeSearchPoint *p; /* Search point for the the leaf */
|
||||
RtreeSearchPoint *p; /* Search point for the leaf */
|
||||
i64 iRowid = sqlite3_value_int64(argv[0]);
|
||||
i64 iNode = 0;
|
||||
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
||||
|
||||
@@ -75,6 +75,9 @@ proc do_common_sql {sql} {
|
||||
}
|
||||
|
||||
proc changeset_from_sql {sql {dbname main}} {
|
||||
if {$dbname == "main"} {
|
||||
return [sql_exec_changeset db $sql]
|
||||
}
|
||||
set rc [catch {
|
||||
sqlite3session S db $dbname
|
||||
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
|
||||
|
||||
@@ -727,12 +727,12 @@ int sqlite3changeset_concat(
|
||||
|
||||
|
||||
/*
|
||||
** Changegroup handle.
|
||||
** CAPI3REF: Changegroup Handle
|
||||
*/
|
||||
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Combine two or more changesets into a single changeset.
|
||||
** CAPI3REF: Create A New Changegroup Object
|
||||
**
|
||||
** An sqlite3_changegroup object is used to combine two or more changesets
|
||||
** (or patchsets) into a single changeset (or patchset). A single changegroup
|
||||
@@ -769,6 +769,8 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add A Changeset To A Changegroup
|
||||
**
|
||||
** Add all changes within the changeset (or patchset) in buffer pData (size
|
||||
** nData bytes) to the changegroup.
|
||||
**
|
||||
@@ -844,6 +846,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
||||
**
|
||||
** Obtain a buffer containing a changeset (or patchset) representing the
|
||||
** current contents of the changegroup. If the inputs to the changegroup
|
||||
** were themselves changesets, the output is a changeset. Or, if the
|
||||
@@ -872,7 +876,7 @@ int sqlite3changegroup_output(
|
||||
);
|
||||
|
||||
/*
|
||||
** Delete a changegroup object.
|
||||
** CAPI3REF: Delete A Changegroup Object
|
||||
*/
|
||||
void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
|
||||
|
||||
+121
-17
@@ -29,6 +29,107 @@ struct TestStreamInput {
|
||||
int iData; /* Bytes of data already read by sessions */
|
||||
};
|
||||
|
||||
/*
|
||||
** Extract an sqlite3* db handle from the object passed as the second
|
||||
** argument. If successful, set *pDb to point to the db handle and return
|
||||
** TCL_OK. Otherwise, return TCL_ERROR.
|
||||
*/
|
||||
static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
|
||||
Tcl_CmdInfo info;
|
||||
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
|
||||
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
*pDb = *(sqlite3 **)info.objClientData;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** The following code is copied byte-for-byte from the sessions module
|
||||
** documentation. It is used by some of the sessions modules tests to
|
||||
** ensure that the example in the documentation does actually work.
|
||||
*/
|
||||
/*
|
||||
** Argument zSql points to a buffer containing an SQL script to execute
|
||||
** against the database handle passed as the first argument. As well as
|
||||
** executing the SQL script, this function collects a changeset recording
|
||||
** all changes made to the "main" database file. Assuming no error occurs,
|
||||
** output variables (*ppChangeset) and (*pnChangeset) are set to point
|
||||
** to a buffer containing the changeset and the size of the changeset in
|
||||
** bytes before returning SQLITE_OK. In this case it is the responsibility
|
||||
** of the caller to eventually free the changeset blob by passing it to
|
||||
** the sqlite3_free function.
|
||||
**
|
||||
** Or, if an error does occur, return an SQLite error code. The final
|
||||
** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
|
||||
*/
|
||||
int sql_exec_changeset(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zSql, /* SQL script to execute */
|
||||
int *pnChangeset, /* OUT: Size of changeset blob in bytes */
|
||||
void **ppChangeset /* OUT: Pointer to changeset blob */
|
||||
){
|
||||
sqlite3_session *pSession = 0;
|
||||
int rc;
|
||||
|
||||
/* Create a new session object */
|
||||
rc = sqlite3session_create(db, "main", &pSession);
|
||||
|
||||
/* Configure the session object to record changes to all tables */
|
||||
if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
|
||||
|
||||
/* Execute the SQL script */
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
|
||||
/* Collect the changeset */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
|
||||
}
|
||||
|
||||
/* Delete the session object */
|
||||
sqlite3session_delete(pSession);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
** Tclcmd: sql_exec_changeset DB SQL
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sql_exec_changeset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
void *pChangeset;
|
||||
int nChangeset;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
|
||||
zSql = (const char*)Tcl_GetString(objv[2]);
|
||||
|
||||
rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
|
||||
sqlite3_free(pChangeset);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
|
||||
|
||||
/*
|
||||
@@ -919,23 +1020,26 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
}
|
||||
|
||||
int TestSession_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_invert", test_sqlite3changeset_invert, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_concat", test_sqlite3changeset_concat, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all, 0, 0
|
||||
);
|
||||
struct Cmd {
|
||||
const char *zCmd;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3session", test_sqlite3session },
|
||||
{ "sqlite3session_foreach", test_sqlite3session_foreach },
|
||||
{ "sqlite3changeset_invert", test_sqlite3changeset_invert },
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
|
||||
{ "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all },
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
|
||||
struct Cmd *p = &aCmd[i];
|
||||
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
C Version\s3.14
|
||||
D 2016-08-08T13:40:27.974
|
||||
C Fix\sa\sproblem\shandling\sexpressions\slike\s"(a,\sb)\sIN\s(SELECT\s...\sORDER\sBY\s1,\s2)"\swhen\sthere\sis\san\sindex\son\s"a"\sbut\snot\s"b".
|
||||
D 2016-09-07T19:37:20.043
|
||||
F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
|
||||
F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION cb29eb11e493dd85b3eeec4053c03949bf98478e
|
||||
F VERSION 25e2e333adeff5965520bc8db999c658898c972d
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
|
||||
F autoconf/Makefile.am 1a47d071e3d5435f8f7ebff7eb6703848bbd65d4
|
||||
F autoconf/Makefile.msc 7a909aa9b538138a21c0626e3ffc4b1f4519f50e
|
||||
F autoconf/Makefile.msc a535edafb6165629715b0ba8e21c98ab80732e80
|
||||
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
|
||||
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
|
||||
F autoconf/configure.ac cacf2616abf6e4a569bde2ef365c143caeec40bc
|
||||
@@ -30,7 +30,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 35ce04a15ca046262bf9baaa2ced9337708cc653 x
|
||||
F configure 3f44893bc0e51c30653e02b0fd4dc2ab585e446a x
|
||||
F configure.ac b5d3df43161374f8dffd2e5f4b88fbb51685b975
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html e2118945e5f07ed146b45c9cd2b2dd6eabb8ebf2
|
||||
@@ -98,13 +98,13 @@ F ext/fts3/unicode/mkunicode.tcl 2debed3f582d77b3fdd0b8830880250021571fd8
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h 62f3e33ceeb9a428db139f9c012186b371da1cc7
|
||||
F ext/fts5/fts5Int.h 9bd0c7c64285b5b368eca0ac63613185c5ad24ba
|
||||
F ext/fts5/fts5_aux.c daa57fb45216491814520bbb587e97bf81ced458
|
||||
F ext/fts5/fts5Int.h b2eda36e0f224365c8e23dc8f559311834f1c13f
|
||||
F ext/fts5/fts5_aux.c 2f20784a344701d4c72986e2e692062dd47d568c
|
||||
F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd
|
||||
F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
|
||||
F ext/fts5/fts5_expr.c bcb238ee4ac1164302ab528487520488516bd030
|
||||
F ext/fts5/fts5_expr.c 1ee97156421919e497595bfa962bb88ad1665401
|
||||
F ext/fts5/fts5_hash.c 880998e596b60f078348d48732ca4ad9a90caad2
|
||||
F ext/fts5/fts5_index.c b429e23fabb57506f71e406997cc46b89190dc97
|
||||
F ext/fts5/fts5_index.c 2d146d5c547f60d22d6fc4014d5e2b64248cd7c4
|
||||
F ext/fts5/fts5_main.c f85281445dcf8be32d18841c93a6f90fe27dbfe2
|
||||
F ext/fts5/fts5_storage.c de0ed8a06738bde433afe11e92295ceaffbc4e58
|
||||
F ext/fts5/fts5_tcl.c 4a901f00c8553740dba63511603f5527d741c26a
|
||||
@@ -114,7 +114,7 @@ F ext/fts5/fts5_tokenize.c 2ce7b44183538ec46b7907726262ee43ffdd39a8
|
||||
F ext/fts5/fts5_unicode2.c b450b209b157d598f7b9df9f837afb75a14c24bf
|
||||
F ext/fts5/fts5_varint.c a5aceacda04dafcbae725413d7a16818ecd65738
|
||||
F ext/fts5/fts5_vocab.c dba72ca393d71c2588548b51380387f6b44c77a8
|
||||
F ext/fts5/fts5parse.y fcc5e92e570d38cab38488b2109cbf67468923b2
|
||||
F ext/fts5/fts5parse.y e51b375403421b8b37428a89b095d00597129aae
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841
|
||||
F ext/fts5/test/fts5aa.test bd2d88182b9f7f30d300044048ad14683306b745
|
||||
@@ -122,7 +122,7 @@ F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b
|
||||
F ext/fts5/test/fts5ac.test 55cad4275a1f5acabfe14d8442a8046b47e49e5f
|
||||
F ext/fts5/test/fts5ad.test 36995f0586f30f5602074e012b9224c71ec5171c
|
||||
F ext/fts5/test/fts5ae.test 612dcb51f4069226791ff14c17dbfb3138c56f20
|
||||
F ext/fts5/test/fts5af.test be858a96b1f5de66ba6d64f0021bd8b2408e126c
|
||||
F ext/fts5/test/fts5af.test c92825778ed2adc80014832762c056bbe968ef88
|
||||
F ext/fts5/test/fts5ag.test 27180de76c03036be75ee80b93d8c5f540014071
|
||||
F ext/fts5/test/fts5ah.test dfb7897711dbcda1dacb038aec310daca139fcf5
|
||||
F ext/fts5/test/fts5ai.test 3909d0b949b2afcaae4d5795cd79153da75381df
|
||||
@@ -135,12 +135,13 @@ F ext/fts5/test/fts5aux.test 5dd158a1e7869e27e9762a2a452b189c728d1be3
|
||||
F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e
|
||||
F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb
|
||||
F ext/fts5/test/fts5bigtok.test 017a9397b14e7598883a6328ead4a6539b42d59a
|
||||
F ext/fts5/test/fts5colset.test 1cdf56e079316005aabda790059aee86f2222ee4
|
||||
F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07
|
||||
F ext/fts5/test/fts5config.test 7788b9c058074d640dfcdd81d97b6a9480000368
|
||||
F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5
|
||||
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/fts5corrupt2.test 128eb6e2d26b09f4da339e581f424b3321e0fdaa
|
||||
F ext/fts5/test/fts5corrupt3.test f77f65e386231daf62902466b40ff998b2c8ce4f
|
||||
F ext/fts5/test/fts5detail.test ef5c690535a797413acaf5ad9b8ab5d49972df69
|
||||
F ext/fts5/test/fts5determin.test 10648edb75ef1e196b10978fd21a9be0c31e09c3
|
||||
@@ -189,7 +190,7 @@ F ext/fts5/test/fts5tok1.test beb894c6f3468f10a574302f69ebe4436b0287c7
|
||||
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
F ext/fts5/test/fts5unicode2.test c1dd890ba32b7609adba78e420faa847abe43b59
|
||||
F ext/fts5/test/fts5unicode2.test 529ac7e8648c943bc87bfed1e427128a2f3f9e33
|
||||
F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
|
||||
F ext/fts5/test/fts5update.test 57c7012a7919889048947addae10e0613df45529
|
||||
@@ -244,20 +245,20 @@ F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
|
||||
F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
|
||||
F ext/rbu/rbu_common.tcl a38e8e2d4a50fd6aaf151633714c1b1d2fae3ead
|
||||
F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695
|
||||
F ext/rbu/rbudiff.test b3c7675810b81de98a930a87fcd40d9ae545619d
|
||||
F ext/rbu/rbudiff.test d099b56b073a737cfe1b8e9f67b77940130719cb
|
||||
F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
|
||||
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
|
||||
F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
|
||||
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
|
||||
F ext/rbu/rbuprogress.test 2023a7df2c523e3df1cb532eff811cda385a789a
|
||||
F ext/rbu/rbuprogress.test e3e25fb7622641b8f2df7c6b7a7eb6fddfc46a4b
|
||||
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
|
||||
F ext/rbu/rbuvacuum.test 4a977447c15c2581ab668781d9ef4294382530e0
|
||||
F ext/rbu/rbuvacuum2.test 45009e127c3fb385e5c0fd5a8a63fb922a79d0ab
|
||||
F ext/rbu/sqlite3rbu.c 948677ee0ec57da51148e6c5f64ac68afcf36ab2
|
||||
F ext/rbu/sqlite3rbu.h db8858120c9be14b60c9225f9da28221f5f6b945
|
||||
F ext/rbu/test_rbu.c 1a6bbc6982e32485a48df111d0bb1934d537eabd
|
||||
F ext/rbu/rbuvacuum2.test 2569205b74ff40fbf3bda2fce33a58eb40eebdcc
|
||||
F ext/rbu/sqlite3rbu.c e074c38798b90591f7f0cf0032d62f152ce5a95e
|
||||
F ext/rbu/sqlite3rbu.h 6fb6294c34a9ca93b5894a33bca530c6f08decba
|
||||
F ext/rbu/test_rbu.c 5aa22616afac6f71ebd3d9bc9bf1006cfabcca88
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c d26a815b0df1c412a6881dae8d7fd3c9c08cce68
|
||||
F ext/rtree/rtree.c 0b450226001c8ae4622e382b2aff79127581a763
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -296,12 +297,12 @@ F ext/session/sessionD.test d4744c78334162851d2a2f285c7e603e31b49aa2
|
||||
F ext/session/sessionE.test e60a238c47f0feb3bb707e7f35e22be09c7e8f26
|
||||
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
|
||||
F ext/session/sessionG.test 01ef705096a9d3984eebdcca79807a211dee1b60
|
||||
F ext/session/session_common.tcl a1293167d14774b5e728836720497f40fe4ea596
|
||||
F ext/session/session_common.tcl 9b696a341cf1d3744823715ed92bb19749b6c3d4
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
|
||||
F ext/session/sqlite3session.c 37485891b4add26cf61495df193c419f36556a32
|
||||
F ext/session/sqlite3session.h 69bf73cfd71e58f2ae5d2aa935b2c1a541aee555
|
||||
F ext/session/test_session.c 2caed9a659586428c63ca46e4900347b374487d4
|
||||
F ext/session/sqlite3session.h 7b9037818ee61f7429ca83e9866885ca6de5f764
|
||||
F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386
|
||||
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
@@ -320,35 +321,36 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c cc28ab933ae615b22add0d609794ffb6596b42ea
|
||||
F src/analyze.c 37fedc80ac966ce1745811746e68e4d8fa64c7fe
|
||||
F src/attach.c 771153bd1f4ab0b97a44a13dde2c7e5e1efeba22
|
||||
F src/auth.c 5c8e0f37f785f935f589496801edd19840485853
|
||||
F src/backup.c 17cd25a36d49330df2bacd2cadf2a61f3b525976
|
||||
F src/alter.c 299117695b1f21ac62dfc5b608588810ba22ed0d
|
||||
F src/analyze.c 8b62b2cf4da85451534ac0af82cafc418d837f68
|
||||
F src/attach.c 4711ff365df4072b8c3dcd55db5d12dcf8ffa0c6
|
||||
F src/auth.c 930b376a9c56998557367e6f7f8aaeac82a2a792
|
||||
F src/backup.c 92c2e3b5fcb47626413717138617f4d32f08aea4
|
||||
F src/bitvec.c 3ee4c8b2c94ed3a7377256e18199e6ff5cf33f63
|
||||
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
|
||||
F src/btree.c 2551bd3ecb8b8988fb8b23aabadfb214dbc38e46
|
||||
F src/btree.h 075c45707c0f8f8af118f739f36df8098a08b7da
|
||||
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
|
||||
F src/build.c 7c3c780b703c09314032c8f6e4e7c1d80241a818
|
||||
F src/build.c c2ccfcdd99e18894a8750e215b8d9c5398a3c073
|
||||
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7
|
||||
F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b
|
||||
F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0
|
||||
F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
|
||||
F src/expr.c 9c5eca8602f6c496e8d4eefefe2aae3d831dd510
|
||||
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
|
||||
F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05
|
||||
F src/expr.c 028c34005cb804abe8f73453ac08baa44f4b63f9
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413
|
||||
F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8
|
||||
F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771
|
||||
F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015
|
||||
F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/insert.c 8f4e9fcbd8e95e85f15647ba8b413b18d556ec2b
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 3edb5a1bda44df13531fedfcde5fbcc2fc04c222
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec
|
||||
F src/main.c 16c1b2114eae8804caf3a8de8cb47bf2c6d83ad3
|
||||
F src/main.c 9821bb4d2399bc5a0b8a5dfc06bede2d1520255c
|
||||
F src/malloc.c 1443d1ad95d67c21d77af7ae3f44678252f0efec
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
@@ -367,33 +369,33 @@ F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24
|
||||
F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343
|
||||
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
|
||||
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c a9443cdab41d7f3cdf0df3a5aab62fd6e1c9b234
|
||||
F src/os_unix.c be9ca0f901a2b6c1bc93dc338f4863675180c189
|
||||
F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e
|
||||
F src/pager.c bf5b71bde3e9b6110e7d6990607db881f6a471a2
|
||||
F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d
|
||||
F src/parse.y 99b676e6fc2f4e331ab93e76b3987cffdbd28efa
|
||||
F src/parse.y 0e0b6d46a990d01e4ca1e9d7e1d2d9b5a98f6bcb
|
||||
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
|
||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||
F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d
|
||||
F src/pragma.c c8b499756658cb8b82cfdbb5845c22cf11f297aa
|
||||
F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812
|
||||
F src/pragma.c d932ba278654617cdd281f88a790a3185fca7c44
|
||||
F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
|
||||
F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e
|
||||
F src/prepare.c 0fcf16eaacc90c1059055519a76b75b516a59a88
|
||||
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 0392c6686586b1d4dac9a4106959f03ddd70e9aa
|
||||
F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c f3c6e9065fb34f6a23af27ec7f1f717ffbfc2ee4
|
||||
F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7
|
||||
F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99
|
||||
F src/select.c 244f9cc5e4662987cd2ef5c22d1b7027560f3425
|
||||
F src/shell.c de7c7e98846cacbfbe062cbd98bca899dfb720e3
|
||||
F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
F src/sqliteInt.h d6f221a5bd572df935140beda82f357c2185a77c
|
||||
F src/sqliteInt.h c9e010a79ab4ed7bdc910a24d8f08f3c6d5f822c
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c bdae822f21e229b6daced15938b6343ce44ef454
|
||||
F src/test1.c 0a0909cf7962d2359db329c08d15b90b4b6e724f
|
||||
F src/test1.c 8574e41c1bf103727909b37351b3690cc07bc8a7
|
||||
F src/test2.c b7174313e993754303a8b33c43df7c44b46857ab
|
||||
F src/test3.c 1339a40be39650ae83894b6578f971dc7f96ea8a
|
||||
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
|
||||
@@ -411,7 +413,7 @@ F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c 4d3d4a886416f369771d69a6dba926866deda788
|
||||
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
|
||||
F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58
|
||||
F src/test_fs.c ac62ce7d5c0c23aa6932891cad5746945564c91c
|
||||
F src/test_fs.c e16cbe68d3b107e00a907c20a9a02629870eb69b
|
||||
F src/test_func.c a2b4993da973b0ea60dd5d51a7066238ede8c329
|
||||
F src/test_hexio.c 1d4469ca61ab202a1fcec6543f584d2407205e8d
|
||||
F src/test_init.c 4413c211a94b62157ca4c145b3f27c497f03c664
|
||||
@@ -438,35 +440,35 @@ F src/test_tclvar.c df9fe1213c2634687a9ca0b0bec0d2119d359ae3
|
||||
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
|
||||
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_windirent.c 8f5fada630348558d5745b334702f301da1ffc61
|
||||
F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5
|
||||
F src/test_windirent.c 600398db0198ca1c77ca183831bf456746b6f5c4
|
||||
F src/test_windirent.h 7edc57e2faa727026dbd5d010dd0e2e665d5aa01
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 3b29883b0ce4a6c6f643965b66b5ca6613178e59
|
||||
F src/treeview.c c56d6ddbed564efda746236b35bcbb8238daac4b
|
||||
F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280
|
||||
F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd
|
||||
F src/tokenize.c 78c8085bc7af1922aa687f0f4bbd716821330de5
|
||||
F src/treeview.c f51b75a28b377adde9f79bc3deb6c7770bcf97c0
|
||||
F src/trigger.c 11e20b3b12c847b3b9055594c0f1631266bb53fc
|
||||
F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802
|
||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
|
||||
F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43
|
||||
F src/vdbe.c ea260b61e73b11a71e70b28a8e25866e2899e5da
|
||||
F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3
|
||||
F src/vdbe.c 3148d5d47816c5ad2ed3c62beb3086cbbcaab107
|
||||
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
|
||||
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
|
||||
F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b
|
||||
F src/vdbeaux.c a32d79aeaa88dc2b97c261172d952d395254a055
|
||||
F src/vdbeblob.c 83d2d266383157b02e2b809350bb197e89d7895b
|
||||
F src/vdbemem.c 1ecaa5ee0caff07255f25d04e8dc88befb6f88d1
|
||||
F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3
|
||||
F src/vdbeaux.c 83458783d241cfd6691141c8a105832ee50258e5
|
||||
F src/vdbeblob.c 3e82a797b60c3b9fed7b8de8c539ca7607874937
|
||||
F src/vdbemem.c e67dc6d8177fd1830efb5d15e17793408251a187
|
||||
F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c
|
||||
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
|
||||
F src/vtab.c 6b3cfaff7e4397739d6b48511e777ca58c6d06d4
|
||||
F src/vtab.c e02cacb5c7ae742631edeb9ae9f53d399f093fd8
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
||||
F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
|
||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||
F src/where.c 5c9df42d50888be8274a5a0eb062eb0629869bd3
|
||||
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
|
||||
F src/wherecode.c 99707d11907c71d289ee9553d2d1a22f1fd8ba41
|
||||
F src/whereexpr.c d7dcbf14ce1b5876c1f76496162c30fcba669563
|
||||
F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09
|
||||
F src/where.c edbd73a87ba2e186928e9bfc14348b1bbb2628c5
|
||||
F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613
|
||||
F src/wherecode.c d172dcf99932ba698dd304edc9a368cd52b4b2e5
|
||||
F src/whereexpr.c e3db778ed205e982f31960896db71c50612ae009
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@@ -523,7 +525,7 @@ F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32
|
||||
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
|
||||
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
|
||||
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
|
||||
F test/backup_malloc.test 833d1b90561a6dbab00079b9591bd4fc90b7c2e1
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
|
||||
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
|
||||
@@ -624,7 +626,7 @@ F test/date.test 984ac1e3e5e031386866f034006148d3972b4a65
|
||||
F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
|
||||
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
|
||||
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
||||
F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f
|
||||
F test/delete.test acc38fca8ee4851467705b1c2cfea64cd26667e5
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
|
||||
F test/delete4.test 21d2113217eeaacac2d99defe14fe6611615ae86
|
||||
@@ -643,7 +645,7 @@ F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
|
||||
F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
|
||||
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
|
||||
F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
|
||||
F test/e_expr.test 03a84a6fa9bd3472112d6bd4599f5269f5f74803
|
||||
F test/e_expr.test 1ffa8866d38e7becc76893a8829e9432050e5716
|
||||
F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707
|
||||
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
|
||||
F test/e_insert.test 3de217e95094d3d165992a6de1164bbc4bd92dc7
|
||||
@@ -654,7 +656,7 @@ F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
|
||||
F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10
|
||||
F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528
|
||||
F test/e_uri.test 25385396082b67fd02ae0038b95a3b3575fe0519
|
||||
F test/e_vacuum.test 120f29ea56bdce4d43279527ece894ab5d1729d3
|
||||
F test/e_vacuum.test 9e5e47e4059a779c777f47e0f560fc82c99336df
|
||||
F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625
|
||||
F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8
|
||||
F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0
|
||||
@@ -822,7 +824,7 @@ F test/hook.test 3b7b99d0eece6d279812c2aef6fa08bdfabc633e
|
||||
F test/icu.test 73956798bace8982909c00476b216714a6d0559a
|
||||
F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
|
||||
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
|
||||
F test/in.test 61d940ced6817bee66e4e0b09d5bc8608f57134b
|
||||
F test/in.test 20c5529986998949908f889c8208b2cd894b2cc9
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
|
||||
@@ -888,7 +890,7 @@ F test/like.test 81632c437a947bf1f7130b19537da6a1a844806a
|
||||
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
|
||||
F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
|
||||
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
|
||||
F test/limit2.test 40e79f3d95f2077742aa5b7676f58c9af1735d83
|
||||
F test/limit2.test ac97b8d07060a0280162ba4159e4c0c765861127
|
||||
F test/loadext.test d077450695ddb5c1ea3ad7d48e5f5850fe732ad9
|
||||
F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7
|
||||
F test/lock.test be4fe08118fb988fed741f429b7dd5d65e1c90db
|
||||
@@ -941,7 +943,7 @@ F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
|
||||
F test/misc5.test fff0f75e834bc09a39f6079320dd8c37de956f4f
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
|
||||
F test/misc8.test 21ac9d35a5e110279ae9e1588b8914f54de1c60b
|
||||
F test/misc8.test ba03aaa08f02d62fbb8d3b2f5595c1b33aa9bbc5
|
||||
F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912
|
||||
F test/mmap1.test d2cfc1635171c434dcff0ece2f1c8e0a658807ce
|
||||
F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022
|
||||
@@ -1018,6 +1020,16 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
|
||||
F test/rowvalue.test 753eb744b7efeb5ac643d35d6e1e5066452ccf79
|
||||
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
|
||||
F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c
|
||||
F test/rowvalue4.test 4b556d7de161a0dd8cff095c336e913986398bea
|
||||
F test/rowvalue5.test c1adfb2ea104e181f70d55bbd80d803b9917b22b
|
||||
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
|
||||
F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8
|
||||
F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0
|
||||
F test/rowvalue9.test e24f9eb02baffc6a67b6eed9e40d4c612c98079d
|
||||
F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
|
||||
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
|
||||
@@ -1041,7 +1053,7 @@ F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
|
||||
F test/select4.test 5389d9895968d1196c457d59b3ee6515d771d328
|
||||
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
|
||||
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
|
||||
F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61
|
||||
F test/select7.test f659f231489349e8c5734e610803d7654207318f
|
||||
F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
|
||||
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
|
||||
F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709
|
||||
@@ -1068,7 +1080,7 @@ F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test 65b10cd8a90cda9b5af9100a45689a57dcc01a31
|
||||
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
|
||||
F test/shell3.test da513d522ef6f01cee8475dcf8332bff8982b3dd
|
||||
F test/shell4.test 69995ee1cc278eb149aa8746ce1f935f4eaf98b9
|
||||
F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
|
||||
F test/shell5.test 50a732c1c2158b1cd62cf53975ce1ea7ce6b9dc9
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
@@ -1093,7 +1105,7 @@ F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
|
||||
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
|
||||
F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8
|
||||
F test/speed2.test 53177056baf6556dcbdcf032bbdfc41c1aa74ded
|
||||
F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
|
||||
F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
@@ -1109,7 +1121,7 @@ F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
|
||||
F test/stmt.test 64844332db69cf1a735fcb3e11548557fc95392f
|
||||
F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
|
||||
F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f
|
||||
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
|
||||
F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
|
||||
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
|
||||
@@ -1129,7 +1141,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
|
||||
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl 949b4a73bd0324b7c796818d0d6a6715712932b3
|
||||
F test/tester.tcl 4ce5afd5e192db4cae178e1a983b060e0f08c5d6
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1312,7 +1324,7 @@ F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
|
||||
F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f
|
||||
F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
@@ -1326,6 +1338,7 @@ F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b
|
||||
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
|
||||
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/vacuum5.test 99d4a0629252f9ef2fc642caefe92436a744db3a
|
||||
F test/vacuummem.test e53a3fdca4612a99c515e1afe7934728a2383764
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
@@ -1344,8 +1357,8 @@ F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
|
||||
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
|
||||
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
|
||||
F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe
|
||||
F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
|
||||
F test/vtabH.test 5f5157a1501d9889ec35c1a1832f69612dd31444
|
||||
F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b
|
||||
F test/vtabH.test 97f61b0253260831af6232163f7852e2653baed6
|
||||
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
|
||||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
@@ -1423,7 +1436,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
|
||||
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
|
||||
F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372
|
||||
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
||||
F tool/addopcodes.tcl 2b089684eb8b7d0db64cf9d8e6d2fe1b6d279e8d
|
||||
F tool/addopcodes.tcl 10c889c4a65ec6c5604e4a47306fa77ff57ae189
|
||||
F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x
|
||||
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
|
||||
F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
|
||||
@@ -1432,12 +1445,12 @@ F tool/dbhash.c a06228aa21ebc4e6ea8daa486601d938499238a5
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
|
||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
F tool/fuzzershell.c 94019b185caceffc9f7c7b678a6489e42bc2aefa
|
||||
F tool/fuzzershell.c f294ca67a10e87db76af130d75b2c94be36359c6
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 09a96bed19955697a5e20c49ad863ec2005815a2
|
||||
F tool/lempar.c 57ffa9852901f6abc45981f0d882f31d1ccb06c0
|
||||
F tool/lemon.c e4fb7d888873ac88f20a41c84a7d1e61f5209a6d
|
||||
F tool/lempar.c 147e42a5cd83ce38275fde0d07a5df3330cb9b3b
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
|
||||
@@ -1476,7 +1489,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 4478f0d30230de6adde90bdb0bfe60f68c5ab782
|
||||
F tool/sqldiff.c 7f567367d87fdb493e3e65169569a10d9f330c3f
|
||||
F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f
|
||||
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
@@ -1509,10 +1522,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 ebc396a19fa79bea208ecda277ffff5d02166d0b
|
||||
R e7962d3372d6a1e29e3980753a6f0162
|
||||
T +bgcolor * #d0c0ff
|
||||
T +sym-release *
|
||||
T +sym-version-3.14.0 *
|
||||
U drh
|
||||
Z ecb9b7072c2b71d36f58909f83563ec2
|
||||
P bbab9621f512b04684163b98b6fc669c68038044
|
||||
R 09b192e18dc26d97d5a589656b60205f
|
||||
U dan
|
||||
Z 3d9ebf68fced17edbc91ddfab98198fe
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
d5e98057028abcf7217d0d2b2e29bbbcdf09d6de
|
||||
7f2c5c9ee3628c968306a5ab2e5a9a761f1b8055
|
||||
+2
-2
@@ -413,7 +413,7 @@ void sqlite3AlterRenameTable(
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( !pTab ) goto exit_rename_table;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
db->flags |= SQLITE_PreferBuiltin;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
@@ -611,7 +611,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
|
||||
pCol = &pNew->aCol[pNew->nCol-1];
|
||||
pDflt = pCol->pDflt;
|
||||
|
||||
+6
-6
@@ -210,14 +210,14 @@ static void openStatTable(
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
|
||||
if( aTable[i].zCols ){
|
||||
/* The sqlite_statN table does not exist. Create it. Note that a
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse->regRoot. This is important
|
||||
** because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
|
||||
);
|
||||
aRoot[i] = pParse->regRoot;
|
||||
aCreateTbl[i] = OPFLAG_P2ISREG;
|
||||
@@ -232,7 +232,7 @@ static void openStatTable(
|
||||
if( zWhere ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q",
|
||||
pDb->zName, zTab, zWhereType, zWhere
|
||||
pDb->zDbSName, zTab, zWhereType, zWhere
|
||||
);
|
||||
}else{
|
||||
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
||||
@@ -994,7 +994,7 @@ static void analyzeOneTable(
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1384,7 +1384,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
|
||||
/* Form 3: Analyze the fully qualified table name */
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
|
||||
if( iDb>=0 ){
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
z = sqlite3NameFromToken(db, pTableName);
|
||||
if( z ){
|
||||
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
|
||||
@@ -1844,7 +1844,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db->aDb[iDb].zName;
|
||||
sInfo.zDatabase = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
|
||||
+5
-5
@@ -97,7 +97,7 @@ static void attachFunc(
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zName;
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
@@ -162,8 +162,8 @@ static void attachFunc(
|
||||
sqlite3BtreeLeave(aNew->pBt);
|
||||
}
|
||||
aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
aNew->zName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zName==0 ){
|
||||
aNew->zDbSName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zDbSName==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ static void detachFunc(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ) continue;
|
||||
if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
|
||||
if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
|
||||
}
|
||||
|
||||
if( i>=db->nDb ){
|
||||
@@ -433,7 +433,7 @@ void sqlite3FixInit(
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zDb = db->aDb[iDb].zDbSName;
|
||||
pFix->pSchema = db->aDb[iDb].pSchema;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
|
||||
+3
-3
@@ -107,9 +107,9 @@ int sqlite3AuthReadCol(
|
||||
const char *zCol, /* Column name */
|
||||
int iDb /* Index of containing database. */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zName; /* Name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
|
||||
|
||||
+18
-9
@@ -196,7 +196,6 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
p->isAttached = 0;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest
|
||||
|| setDestPgsz(p)==SQLITE_NOMEM
|
||||
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
|
||||
){
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
@@ -384,14 +383,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* If there is no open read-transaction on the source database, open
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
@@ -401,6 +392,24 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
/* If the destination database has not yet been locked (i.e. if this
|
||||
** is the first call to backup_step() for the current backup operation),
|
||||
** try to set its page size to the same as the source database. This
|
||||
** is especially important on ZipVFS systems, as in that case it is
|
||||
** not possible to create a database file that uses one page size by
|
||||
** writing to it with another. */
|
||||
if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* Do not allow backup if the destination database is in WAL mode
|
||||
** and the page sizes are different between source and destination */
|
||||
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
|
||||
|
||||
+30
-29
@@ -153,8 +153,8 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
if( pParse->nTableLock>0 && db->init.busy==0 ){
|
||||
sqlite3UserAuthInit(db);
|
||||
if( db->auth.authLevel<UAUTH_User ){
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
sqlite3ErrorMsg(pParse, "user not authenticated");
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -318,10 +318,11 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
#endif
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -395,7 +396,7 @@ Table *sqlite3LocateTableItem(
|
||||
assert( p->pSchema==0 || p->zDatabase==0 );
|
||||
if( p->pSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
|
||||
zDb = pParse->db->aDb[iDb].zName;
|
||||
zDb = pParse->db->aDb[iDb].zDbSName;
|
||||
}else{
|
||||
zDb = p->zDatabase;
|
||||
}
|
||||
@@ -423,7 +424,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
Schema *pSchema = db->aDb[j].pSchema;
|
||||
assert( pSchema );
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&pSchema->idxHash, zName);
|
||||
if( p ) break;
|
||||
@@ -492,8 +493,8 @@ void sqlite3CollapseDatabaseArray(sqlite3 *db){
|
||||
for(i=j=2; i<db->nDb; i++){
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
sqlite3DbFree(db, pDb->zName);
|
||||
pDb->zName = 0;
|
||||
sqlite3DbFree(db, pDb->zDbSName);
|
||||
pDb->zDbSName = 0;
|
||||
continue;
|
||||
}
|
||||
if( j<i ){
|
||||
@@ -713,7 +714,7 @@ int sqlite3FindDbName(sqlite3 *db, const char *zName){
|
||||
if( zName ){
|
||||
Db *pDb;
|
||||
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
|
||||
if( 0==sqlite3StrICmp(pDb->zName, zName) ) break;
|
||||
if( 0==sqlite3StrICmp(pDb->zDbSName, zName) ) break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
@@ -772,7 +773,7 @@ int sqlite3TwoPartName(
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
assert( db->init.iDb==0 || db->init.busy );
|
||||
assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
|
||||
iDb = db->init.iDb;
|
||||
*pUnqual = pName1;
|
||||
}
|
||||
@@ -883,7 +884,7 @@ void sqlite3StartTable(
|
||||
SQLITE_CREATE_VIEW,
|
||||
SQLITE_CREATE_TEMP_VIEW
|
||||
};
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -902,7 +903,7 @@ void sqlite3StartTable(
|
||||
** collisions.
|
||||
*/
|
||||
if( !IN_DECLARE_VTAB ){
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -1995,7 +1996,7 @@ void sqlite3EndTable(
|
||||
"UPDATE %Q.%s "
|
||||
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
|
||||
"WHERE rowid=#%d",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
zType,
|
||||
p->zName,
|
||||
p->zName,
|
||||
@@ -2010,13 +2011,13 @@ void sqlite3EndTable(
|
||||
/* Check to see if we need to create an sqlite_sequence table for
|
||||
** keeping track of autoincrement keys.
|
||||
*/
|
||||
if( p->tabFlags & TF_Autoincrement ){
|
||||
if( (p->tabFlags & TF_Autoincrement)!=0 ){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( pDb->pSchema->pSeqTab==0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
|
||||
pDb->zName
|
||||
pDb->zDbSName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2330,7 +2331,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
pParse->db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
#endif
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -2406,7 +2407,7 @@ static void sqlite3ClearStatTables(
|
||||
const char *zName /* Name of index or table */
|
||||
){
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zDbSName;
|
||||
for(i=1; i<=4; i++){
|
||||
char zTab[24];
|
||||
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
|
||||
@@ -2459,7 +2460,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
pDb->zDbSName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
@@ -2473,7 +2474,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
pDb->zDbSName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
@@ -2527,7 +2528,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
{
|
||||
int code;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zArg2 = 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
|
||||
goto exit_drop_table;
|
||||
@@ -2768,7 +2769,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -3020,7 +3021,7 @@ void sqlite3CreateIndex(
|
||||
goto exit_create_index;
|
||||
}
|
||||
}
|
||||
if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
|
||||
if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
|
||||
if( !ifNotExist ){
|
||||
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
|
||||
}else{
|
||||
@@ -3050,7 +3051,7 @@ void sqlite3CreateIndex(
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
const char *zDb = pDb->zName;
|
||||
const char *zDb = pDb->zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
@@ -3365,7 +3366,7 @@ void sqlite3CreateIndex(
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
pIndex->zName,
|
||||
pTab->zName,
|
||||
iMem,
|
||||
@@ -3499,7 +3500,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
{
|
||||
int code = SQLITE_DROP_INDEX;
|
||||
Table *pTab = pIndex->pTable;
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
|
||||
goto exit_drop_index;
|
||||
@@ -3517,7 +3518,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
);
|
||||
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@@ -4062,7 +4063,7 @@ void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zDbSName)) ){
|
||||
sqlite3CodeVerifySchema(pParse, i);
|
||||
}
|
||||
}
|
||||
@@ -4309,7 +4310,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
if( iDb<0 ) return;
|
||||
z = sqlite3NameFromToken(db, pObjName);
|
||||
if( z==0 ) return;
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
pTab = sqlite3FindTable(db, z, zDb);
|
||||
if( pTab ){
|
||||
reindexTable(pParse, pTab, 0);
|
||||
|
||||
+2
-2
@@ -602,7 +602,7 @@ static int statFilter(
|
||||
" UNION ALL "
|
||||
"SELECT name, rootpage, type"
|
||||
" FROM \"%w\".%s WHERE rootpage!=0"
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
@@ -656,7 +656,7 @@ static int statColumn(
|
||||
default: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int iDb = pCsr->iDb;
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -102,7 +102,7 @@ void sqlite3MaterializeView(
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
@@ -289,7 +289,7 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
|
||||
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
|
||||
if( rcauth==SQLITE_DENY ){
|
||||
|
||||
+947
-270
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -871,7 +871,7 @@ void sqlite3FkCheck(
|
||||
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
|
||||
/* Loop through all the foreign key constraints for which pTab is the
|
||||
** child table (the table that the foreign key definition is part of). */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
IN-Operator Implementation Notes
|
||||
================================
|
||||
|
||||
## Definitions:
|
||||
|
||||
An IN operator has one of the following formats:
|
||||
|
||||
>
|
||||
x IN (y1,y2,y3,...,yN)
|
||||
x IN (subquery)
|
||||
|
||||
The "x" is referred to as the LHS (left-hand side). The list or subquery
|
||||
on the right is called the RHS (right-hand side). If the RHS is a list
|
||||
it must be a non-empty list. But if the RHS is a subquery, it can be an
|
||||
empty set.
|
||||
|
||||
The LHS can be a scalar (a single quantity) or a vector (a list of
|
||||
two or or more values) or a subquery that returns one or more columns.
|
||||
We use the term "vector" to mean an actually list of values or a
|
||||
subquery that returns two or more columns. An isolated value or
|
||||
a subquery that returns a single columns is called a scalar.
|
||||
|
||||
The RHS can be a subquery that returns a single column, a subquery
|
||||
that returns two or more columns, or a list of scalars. It is not
|
||||
currently support for the RHS to be a list of vectors.
|
||||
|
||||
The number of columns for LHS must match the number of columns for
|
||||
the RHS. If the RHS is a list of values, then the LHS must be a
|
||||
scalar. If the RHS is a subquery returning N columns, then the LHS
|
||||
must be a vector of size N.
|
||||
|
||||
NULL values can occur in either or both of the LHS and RHS.
|
||||
If the LHS contains only
|
||||
NULL values then we say that it is a "total-NULL". If the LHS contains
|
||||
some NULL values and some non-NULL values, then it is a "partial-NULL".
|
||||
For a scalar, there is no difference between a partial-NULL and a total-NULL.
|
||||
The RHS is a partial-NULL if any row contains a NULL value. The RHS is
|
||||
a total-NULL if it contains one or more rows that contain only NULL values.
|
||||
The LHS is called "non-NULL" if it contains no NULL values. The RHS is
|
||||
called "non-NULL" if it contains no NULL values in any row.
|
||||
|
||||
The result of an IN operator is one of TRUE, FALSE, or NULL. A NULL result
|
||||
means that it cannot be determined if the LHS is contained in the RHS due
|
||||
to the presence of NULL values. In some contexts (for example, when the IN
|
||||
operator occurs in a WHERE clause)
|
||||
the system only needs a binary result: TRUE or NOT-TRUE. One can also
|
||||
to define a binary result of FALSE and NOT-FALSE, but
|
||||
it turns out that no extra optimizations are possible in that case, so if
|
||||
the FALSE/NOT-FALSE binary is needed, we have to compute the three-state
|
||||
TRUE/FALSE/NULL result and then combine the TRUE and NULL values into
|
||||
NOT-FALSE.
|
||||
|
||||
A "NOT IN" operator is computed by first computing the equivalent IN
|
||||
operator, then interchanging the TRUE and FALSE results.
|
||||
|
||||
## Simple Full-Scan Algorithm
|
||||
|
||||
The following algorithm always compute the correct answer. However, this
|
||||
algorithm is suboptimal, especially if there are many rows on the RHS.
|
||||
|
||||
1. Set the null-flag to false
|
||||
2. For each row in the RHS:
|
||||
<ol type='a'>
|
||||
<li> Compare the LHS against the RHS
|
||||
<li> If the LHS exactly matches the RHS, immediately return TRUE
|
||||
<li> If the comparison result is NULL, set the null-flag to true
|
||||
</ol>
|
||||
3. If the null-flag is true, return NULL.
|
||||
4. Return FALSE
|
||||
|
||||
## Optimized Algorithm
|
||||
|
||||
The following procedure computes the same answer as the simple full-scan
|
||||
algorithm, though it does so with less work in the common case. This
|
||||
is the algorithm that is implemented in SQLite.
|
||||
|
||||
1. If the RHS is a constant list of length 1 or 2, then rewrite the
|
||||
IN operator as a simple expression. Implement
|
||||
|
||||
x IN (y1,y2)
|
||||
|
||||
as if it were
|
||||
|
||||
x=y1 OR x=y2
|
||||
|
||||
This is the INDEX_NOOP optimization and is only undertaken if the
|
||||
IN operator is used for membership testing. If the IN operator is
|
||||
driving a loop, then skip this step entirely.
|
||||
|
||||
2. Check the LHS to see if it is a partial-NULL and if it is, jump
|
||||
ahead to step 5.
|
||||
|
||||
3. Do a binary search of the RHS using the LHS as a probe. If
|
||||
an exact match is found, return TRUE.
|
||||
|
||||
4. If the RHS is non-NULL then return FALSE.
|
||||
|
||||
5. If we do not need to distinguish between FALSE and NULL,
|
||||
then return FALSE.
|
||||
|
||||
6. For each row in the RHS, compare that row against the LHS and
|
||||
if the result is NULL, immediately return NULL. In the case
|
||||
of a scalar IN operator, we only need to look at the very first
|
||||
row the RHS because for a scalar RHS, all NULLs will always come
|
||||
first. If the RHS is empty, this step is a no-op.
|
||||
|
||||
7. Return FALSE.
|
||||
+12
-8
@@ -200,7 +200,9 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
/*
|
||||
** Locate or create an AutoincInfo structure associated with table pTab
|
||||
** which is in database iDb. Return the register number for the register
|
||||
** that holds the maximum rowid.
|
||||
** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
|
||||
** table. (Also return zero when doing a VACUUM since we do not want to
|
||||
** update the AUTOINCREMENT counters during a VACUUM.)
|
||||
**
|
||||
** There is at most one AutoincInfo structure per table even if the
|
||||
** same table is autoincremented multiple times due to inserts within
|
||||
@@ -223,7 +225,9 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->flags & SQLITE_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
|
||||
@@ -547,7 +551,7 @@ void sqlite3Insert(
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
pDb = &db->aDb[iDb];
|
||||
zDb = pDb->zName;
|
||||
zDb = pDb->zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@@ -1777,15 +1781,15 @@ int sqlite3OpenTableAndIndices(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
int iIdxCur = iBase++;
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
p5 = 0;
|
||||
}
|
||||
if( aToOpen==0 || aToOpen[i+1] ){
|
||||
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
}else{
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
}
|
||||
if( iBase>pParse->nTab ) pParse->nTab = iBase;
|
||||
|
||||
+20
-10
@@ -789,6 +789,11 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
int rc;
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_DBCONFIG_MAINDBNAME: {
|
||||
db->aDb[0].zDbSName = va_arg(ap,char*);
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
}
|
||||
case SQLITE_DBCONFIG_LOOKASIDE: {
|
||||
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
|
||||
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
|
||||
@@ -1838,6 +1843,8 @@ int sqlite3_trace_v2(
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( mTrace==0 ) xTrace = 0;
|
||||
if( xTrace==0 ) mTrace = 0;
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
@@ -2932,9 +2939,9 @@ static int openDatabase(
|
||||
/* The default safety_level for the main database is FULL; for the temp
|
||||
** database it is OFF. This matches the pager layer defaults.
|
||||
*/
|
||||
db->aDb[0].zName = "main";
|
||||
db->aDb[0].zDbSName = "main";
|
||||
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
db->aDb[1].zName = "temp";
|
||||
db->aDb[1].zDbSName = "temp";
|
||||
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
|
||||
|
||||
db->magic = SQLITE_MAGIC_OPEN;
|
||||
@@ -2948,11 +2955,20 @@ static int openDatabase(
|
||||
*/
|
||||
sqlite3Error(db, SQLITE_OK);
|
||||
sqlite3RegisterPerConnectionBuiltinFunctions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
/* Register any built-in FTS5 module before loading the automatic
|
||||
** extensions. This allows automatic extensions to register FTS5
|
||||
** tokenizers and auxiliary functions. */
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
*/
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
@@ -2981,12 +2997,6 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3IcuInit(db);
|
||||
@@ -3895,7 +3905,7 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zDbSName)==0)
|
||||
){
|
||||
return db->aDb[i].pBt;
|
||||
}
|
||||
|
||||
+31
-8
@@ -5529,6 +5529,27 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
return pUnused;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the mode, uid and gid of file zFile.
|
||||
*/
|
||||
static int getFileMode(
|
||||
const char *zFile, /* File name */
|
||||
mode_t *pMode, /* OUT: Permissions of zFile */
|
||||
uid_t *pUid, /* OUT: uid of zFile. */
|
||||
gid_t *pGid /* OUT: gid of zFile. */
|
||||
){
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
int rc = SQLITE_OK;
|
||||
if( 0==osStat(zFile, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by unixOpen() to determine the unix permissions
|
||||
** to create new files with. If no error occurs, then SQLITE_OK is returned
|
||||
@@ -5564,7 +5585,6 @@ static int findCreateFileMode(
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
char zDb[MAX_PATHNAME+1]; /* Database file path */
|
||||
int nDb; /* Number of valid bytes in zDb */
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
|
||||
/* zPath is a path to a WAL or journal file. The following block derives
|
||||
** the path to the associated database file from zPath. This block handles
|
||||
@@ -5595,15 +5615,18 @@ static int findCreateFileMode(
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
if( 0==osStat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
rc = getFileMode(zDb, pMode, pUid, pGid);
|
||||
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
*pMode = 0600;
|
||||
}else if( flags & SQLITE_OPEN_URI ){
|
||||
/* If this is a main database file and the file was opened using a URI
|
||||
** filename, check for the "modeof" parameter. If present, interpret
|
||||
** its value as a filename and try to copy the mode, uid and gid from
|
||||
** that file. */
|
||||
const char *z = sqlite3_uri_parameter(zPath, "modeof");
|
||||
if( z ){
|
||||
rc = getFileMode(z, pMode, pUid, pGid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+19
-1
@@ -6656,7 +6656,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
||||
** savepoint. If no errors occur, SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
int rc = pPager->errCode; /* Return code */
|
||||
int rc = pPager->errCode;
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
|
||||
#endif
|
||||
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
|
||||
@@ -6697,6 +6701,20 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
|
||||
assert(rc!=SQLITE_DONE);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
/* If the cache has been modified but the savepoint cannot be rolled
|
||||
** back journal_mode=off, put the pager in the error state. This way,
|
||||
** if the VFS used by this pager includes ZipVFS, the entire transaction
|
||||
** can be rolled back at the ZipVFS level. */
|
||||
else if(
|
||||
pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
&& pPager->eState>=PAGER_WRITER_CACHEMOD
|
||||
){
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
+19
-2
@@ -789,10 +789,16 @@ setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, A, Y.pExpr);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr);
|
||||
}
|
||||
setlist(A) ::= nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, 0, Y.pExpr);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr);
|
||||
}
|
||||
|
||||
////////////////////////// The INSERT command /////////////////////////////////
|
||||
//
|
||||
@@ -946,6 +952,17 @@ term(A) ::= CTIME_KW(OP). {
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = pList;
|
||||
spanSet(&A, &L, &R);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
||||
@@ -1285,8 +1302,8 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
|
||||
//
|
||||
%ifndef SQLITE_OMIT_VACUUM
|
||||
%ifndef SQLITE_OMIT_ATTACH
|
||||
cmd ::= VACUUM. {sqlite3Vacuum(pParse);}
|
||||
cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
|
||||
cmd ::= VACUUM. {sqlite3Vacuum(pParse,0);}
|
||||
cmd ::= VACUUM nm(X). {sqlite3Vacuum(pParse,&X);}
|
||||
%endif SQLITE_OMIT_ATTACH
|
||||
%endif SQLITE_OMIT_VACUUM
|
||||
|
||||
|
||||
+27
-7
@@ -632,12 +632,30 @@ static void pcache1TruncateUnsafe(
|
||||
PCache1 *pCache, /* The cache to truncate */
|
||||
unsigned int iLimit /* Drop pages with this pgno or larger */
|
||||
){
|
||||
TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */
|
||||
unsigned int h;
|
||||
TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */
|
||||
unsigned int h, iStop;
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
for(h=0; h<pCache->nHash; h++){
|
||||
PgHdr1 **pp = &pCache->apHash[h];
|
||||
assert( pCache->iMaxKey >= iLimit );
|
||||
assert( pCache->nHash > 0 );
|
||||
if( pCache->iMaxKey - iLimit < pCache->nHash ){
|
||||
/* If we are just shaving the last few pages off the end of the
|
||||
** cache, then there is no point in scanning the entire hash table.
|
||||
** Only scan those hash slots that might contain pages that need to
|
||||
** be removed. */
|
||||
h = iLimit % pCache->nHash;
|
||||
iStop = pCache->iMaxKey % pCache->nHash;
|
||||
TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */
|
||||
}else{
|
||||
/* This is the general case where many pages are being removed.
|
||||
** It is necessary to scan the entire hash table */
|
||||
h = pCache->nHash/2;
|
||||
iStop = h - 1;
|
||||
}
|
||||
for(;;){
|
||||
PgHdr1 **pp;
|
||||
PgHdr1 *pPage;
|
||||
assert( h<pCache->nHash );
|
||||
pp = &pCache->apHash[h];
|
||||
while( (pPage = *pp)!=0 ){
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pCache->nPage--;
|
||||
@@ -646,11 +664,13 @@ static void pcache1TruncateUnsafe(
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
TESTONLY( nPage++; )
|
||||
TESTONLY( if( nPage>=0 ) nPage++; )
|
||||
}
|
||||
}
|
||||
if( h==iStop ) break;
|
||||
h = (h+1) % pCache->nHash;
|
||||
}
|
||||
assert( pCache->nPage==nPage );
|
||||
assert( nPage<0 || pCache->nPage==(unsigned)nPage );
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -1127,7 +1147,7 @@ static void pcache1Destroy(sqlite3_pcache *p){
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
|
||||
pcache1EnterMutex(pGroup);
|
||||
pcache1TruncateUnsafe(pCache, 0);
|
||||
if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0);
|
||||
assert( pGroup->nMaxPage >= pCache->nMax );
|
||||
pGroup->nMaxPage -= pCache->nMax;
|
||||
assert( pGroup->nMinPage >= pCache->nMin );
|
||||
|
||||
+7
-7
@@ -338,7 +338,7 @@ void sqlite3Pragma(
|
||||
}
|
||||
|
||||
assert( pId2 );
|
||||
zDb = pId2->n>0 ? pDb->zName : 0;
|
||||
zDb = pId2->n>0 ? pDb->zDbSName : 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
@@ -1191,10 +1191,10 @@ void sqlite3Pragma(
|
||||
setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ) continue;
|
||||
assert( db->aDb[i].zName!=0 );
|
||||
assert( db->aDb[i].zDbSName!=0 );
|
||||
sqlite3VdbeMultiLoad(v, 1, "iss",
|
||||
i,
|
||||
db->aDb[i].zName,
|
||||
db->aDb[i].zDbSName,
|
||||
sqlite3BtreeGetFilename(db->aDb[i].pBt));
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
|
||||
}
|
||||
@@ -1483,7 +1483,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeChangeP5(v, (u8)i);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
|
||||
P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
|
||||
@@ -1922,15 +1922,15 @@ void sqlite3Pragma(
|
||||
Btree *pBt;
|
||||
const char *zState = "unknown";
|
||||
int j;
|
||||
if( db->aDb[i].zName==0 ) continue;
|
||||
if( db->aDb[i].zDbSName==0 ) continue;
|
||||
pBt = db->aDb[i].pBt;
|
||||
if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
|
||||
zState = "closed";
|
||||
}else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0,
|
||||
}else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
|
||||
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
|
||||
zState = azLockName[j];
|
||||
}
|
||||
sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zName, zState);
|
||||
sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zDbSName, zState);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
+6
-4
@@ -73,6 +73,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** structures that describe the table, index, or view.
|
||||
*/
|
||||
int rc;
|
||||
u8 saved_iDb = db->init.iDb;
|
||||
sqlite3_stmt *pStmt;
|
||||
TESTONLY(int rcp); /* Return code from sqlite3_prepare() */
|
||||
|
||||
@@ -83,7 +84,8 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = 0;
|
||||
db->init.iDb = saved_iDb;
|
||||
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
@@ -107,7 +109,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** to do here is record the root page number for that index.
|
||||
*/
|
||||
Index *pIndex;
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
|
||||
if( pIndex==0 ){
|
||||
/* This can occur if there exists an index on a TEMP table which
|
||||
** has the same name as another index on a permanent index. Since
|
||||
@@ -286,7 +288,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
|
||||
db->aDb[iDb].zName, zMasterName);
|
||||
db->aDb[iDb].zDbSName, zMasterName);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
sqlite3_xauth xAuth;
|
||||
@@ -561,7 +563,7 @@ static int sqlite3Prepare(
|
||||
assert( sqlite3BtreeHoldsMutex(pBt) );
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
const char *zDb = db->aDb[i].zDbSName;
|
||||
sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
|
||||
testcase( db->flags & SQLITE_ReadUncommitted );
|
||||
goto end_prepare;
|
||||
|
||||
+29
-2
@@ -221,8 +221,8 @@ static int lookupName(
|
||||
zDb = 0;
|
||||
}else{
|
||||
for(i=0; i<db->nDb; i++){
|
||||
assert( db->aDb[i].zName );
|
||||
if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
|
||||
assert( db->aDb[i].zDbSName );
|
||||
if( sqlite3StrICmp(db->aDb[i].zDbSName,zDb)==0 ){
|
||||
pSchema = db->aDb[i].pSchema;
|
||||
break;
|
||||
}
|
||||
@@ -776,6 +776,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
||||
break;
|
||||
}
|
||||
case TK_EQ:
|
||||
case TK_NE:
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
case TK_GE:
|
||||
case TK_IS:
|
||||
case TK_ISNOT: {
|
||||
int nLeft, nRight;
|
||||
if( pParse->db->mallocFailed ) break;
|
||||
assert( pExpr->pRight!=0 );
|
||||
assert( pExpr->pLeft!=0 );
|
||||
nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||
nRight = sqlite3ExprVectorSize(pExpr->pRight);
|
||||
if( nLeft!=nRight ){
|
||||
testcase( pExpr->op==TK_EQ );
|
||||
testcase( pExpr->op==TK_NE );
|
||||
testcase( pExpr->op==TK_LT );
|
||||
testcase( pExpr->op==TK_LE );
|
||||
testcase( pExpr->op==TK_GT );
|
||||
testcase( pExpr->op==TK_GE );
|
||||
testcase( pExpr->op==TK_IS );
|
||||
testcase( pExpr->op==TK_ISNOT );
|
||||
sqlite3ErrorMsg(pParse, "row value misused");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
|
||||
}
|
||||
|
||||
+32
-66
@@ -88,7 +88,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
||||
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
|
||||
pDest->eDest = (u8)eDest;
|
||||
pDest->iSDParm = iParm;
|
||||
pDest->affSdst = 0;
|
||||
pDest->zAffSdst = 0;
|
||||
pDest->iSdst = 0;
|
||||
pDest->nSdst = 0;
|
||||
}
|
||||
@@ -659,30 +659,6 @@ static void codeDistinct(
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
/*
|
||||
** Generate an error message when a SELECT is used within a subexpression
|
||||
** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
|
||||
** column. We do this in a subroutine because the error used to occur
|
||||
** in multiple places. (The error only occurs in one place now, but we
|
||||
** retain the subroutine to minimize code disruption.)
|
||||
*/
|
||||
static int checkForMultiColumnSelectError(
|
||||
Parse *pParse, /* Parse context. */
|
||||
SelectDest *pDest, /* Destination of SELECT results */
|
||||
int nExpr /* Number of result columns returned by SELECT */
|
||||
){
|
||||
int eDest = pDest->eDest;
|
||||
if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
|
||||
sqlite3ErrorMsg(pParse, "only a single result allowed for "
|
||||
"a SELECT that is part of an expression");
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine generates the code for the inside of the inner loop
|
||||
** of a SELECT.
|
||||
@@ -892,19 +868,19 @@ static void selectInnerLoop(
|
||||
** item into the set table with bogus data.
|
||||
*/
|
||||
case SRT_Set: {
|
||||
assert( nResultCol==1 );
|
||||
pDest->affSdst =
|
||||
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
|
||||
if( pSort ){
|
||||
/* At first glance you would think we could optimize out the
|
||||
** ORDER BY in this case since the order of entries in the set
|
||||
** does not matter. But there might be a LIMIT clause, in which
|
||||
** case the order does matter */
|
||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
||||
pushOntoSorter(
|
||||
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
|
||||
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
||||
r1, pDest->zAffSdst, nResultCol);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -920,13 +896,14 @@ static void selectInnerLoop(
|
||||
}
|
||||
|
||||
/* If this is a scalar select that is part of an expression, then
|
||||
** store the results in the appropriate memory cell and break out
|
||||
** of the scan loop.
|
||||
** store the results in the appropriate memory cell or array of
|
||||
** memory cells and break out of the scan loop.
|
||||
*/
|
||||
case SRT_Mem: {
|
||||
assert( nResultCol==1 );
|
||||
assert( nResultCol==pDest->nSdst );
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
||||
pushOntoSorter(
|
||||
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
|
||||
}else{
|
||||
assert( regResult==iParm );
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
@@ -1241,14 +1218,14 @@ static void generateSortTail(
|
||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||
}
|
||||
iTab = pSort->iECursor;
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
|
||||
regRowid = 0;
|
||||
regRow = pDest->iSdst;
|
||||
nSortData = nColumn;
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
nSortData = 1;
|
||||
regRow = sqlite3GetTempRange(pParse, nColumn);
|
||||
nSortData = nColumn;
|
||||
}
|
||||
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
@@ -1283,16 +1260,14 @@ static void generateSortTail(
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case SRT_Set: {
|
||||
assert( nColumn==1 );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid,
|
||||
&pDest->affSdst, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
|
||||
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
|
||||
pDest->zAffSdst, nColumn);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
||||
break;
|
||||
}
|
||||
case SRT_Mem: {
|
||||
assert( nColumn==1 );
|
||||
sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
|
||||
/* The LIMIT clause will terminate the loop for us */
|
||||
break;
|
||||
}
|
||||
@@ -1311,7 +1286,11 @@ static void generateSortTail(
|
||||
}
|
||||
}
|
||||
if( regRowid ){
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
if( eDest==SRT_Set ){
|
||||
sqlite3ReleaseTempRange(pParse, regRow, nColumn);
|
||||
}else{
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
}
|
||||
/* The bottom of the loop
|
||||
@@ -1458,7 +1437,7 @@ static const char *columnTypeImpl(
|
||||
zOrigTab = pTab->zName;
|
||||
if( pNC->pParse ){
|
||||
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
|
||||
zOrigDb = pNC->pParse->db->aDb[iDb].zName;
|
||||
zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
|
||||
}
|
||||
#else
|
||||
if( iCol<0 ){
|
||||
@@ -1813,7 +1792,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
*/
|
||||
static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
|
||||
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
|
||||
if( v ) sqlite3VdbeAddOp0(v, OP_Init);
|
||||
if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
|
||||
if( pParse->pToplevel==0
|
||||
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
|
||||
){
|
||||
@@ -2652,18 +2631,15 @@ static int generateOutputSubroutine(
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
|
||||
** then there should be a single item on the stack. Write this
|
||||
** item into the set table with bogus data.
|
||||
/* If we are creating a set for an "expr IN (SELECT ...)".
|
||||
*/
|
||||
case SRT_Set: {
|
||||
int r1;
|
||||
assert( pIn->nSdst==1 || pParse->nErr>0 );
|
||||
pDest->affSdst =
|
||||
sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
|
||||
testcase( pIn->nSdst>1 );
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
|
||||
r1, pDest->zAffSdst, pIn->nSdst);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
break;
|
||||
@@ -4414,7 +4390,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
continue;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
|
||||
zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*";
|
||||
}
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
char *zName = pTab->aCol[j].zName;
|
||||
@@ -4897,16 +4873,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* If writing to memory or generating a set
|
||||
** only a single column may be output.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
|
||||
goto select_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Try to flatten subqueries in the FROM clause up into the main query
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
|
||||
+2
-2
@@ -524,7 +524,7 @@ static char *local_getline(char *zLine, FILE *in){
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
/* For interactive input on Windows systems, translate the
|
||||
** multi-byte characterset characters into UTF-8. */
|
||||
if( stdin_is_interactive ){
|
||||
if( stdin_is_interactive && in==stdin ){
|
||||
char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
|
||||
if( zTrans ){
|
||||
int nTrans = strlen30(zTrans)+1;
|
||||
@@ -4905,7 +4905,7 @@ static int process_input(ShellState *p, FILE *in){
|
||||
zLine = one_input_line(in, zLine, nSql>0);
|
||||
if( zLine==0 ){
|
||||
/* End of input */
|
||||
if( stdin_is_interactive ) printf("\n");
|
||||
if( in==0 && stdin_is_interactive ) printf("\n");
|
||||
break;
|
||||
}
|
||||
if( seenInterrupt ){
|
||||
|
||||
@@ -1969,8 +1969,18 @@ struct sqlite3_mem_methods {
|
||||
** be a NULL pointer, in which case the new setting is not reported back.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
||||
** <dd> ^This option is used to change the name of the "main" database
|
||||
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
||||
** which will become the new schema name in place of "main". ^SQLite
|
||||
** does not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into this DBCONFIG option is unchanged
|
||||
** until after the database connection closes.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
|
||||
|
||||
+28
-9
@@ -1097,7 +1097,7 @@ typedef struct With With;
|
||||
** databases may be attached.
|
||||
*/
|
||||
struct Db {
|
||||
char *zName; /* Name of this database */
|
||||
char *zDbSName; /* Name of this database. (schema name, not filename) */
|
||||
Btree *pBt; /* The B*Tree structure for this database file */
|
||||
u8 safety_level; /* How aggressive at syncing data to disk */
|
||||
u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
|
||||
@@ -1733,6 +1733,7 @@ struct CollSeq {
|
||||
** operator is NULL. It is added to certain comparison operators to
|
||||
** prove that the operands are always NOT NULL.
|
||||
*/
|
||||
#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
|
||||
#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
|
||||
#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
|
||||
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
|
||||
@@ -2297,9 +2298,11 @@ struct Expr {
|
||||
int iTable; /* TK_COLUMN: cursor number of table holding column
|
||||
** TK_REGISTER: register number
|
||||
** TK_TRIGGER: 1 -> new, 0 -> old
|
||||
** EP_Unlikely: 134217728 times likelihood */
|
||||
** EP_Unlikely: 134217728 times likelihood
|
||||
** TK_SELECT: 1st register of result vector */
|
||||
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
|
||||
** TK_VARIABLE: variable number (always >= 1). */
|
||||
** TK_VARIABLE: variable number (always >= 1).
|
||||
** TK_SELECT_COLUMN: column of the result vector */
|
||||
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
|
||||
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
|
||||
u8 op2; /* TK_REGISTER: original value of Expr.op
|
||||
@@ -2780,7 +2783,7 @@ struct Select {
|
||||
*/
|
||||
struct SelectDest {
|
||||
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
|
||||
char affSdst; /* Affinity used when eDest==SRT_Set */
|
||||
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
|
||||
int iSDParm; /* A parameter used by the eDest disposal method */
|
||||
int iSdst; /* Base register where results are written */
|
||||
int nSdst; /* Number of registers allocated */
|
||||
@@ -3512,6 +3515,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
|
||||
void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
|
||||
void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
|
||||
void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
|
||||
void sqlite3TreeViewWith(TreeView*, const With*, u8);
|
||||
@@ -3543,6 +3547,7 @@ Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
|
||||
void sqlite3ExprAssignVarNumber(Parse*, Expr*);
|
||||
void sqlite3ExprDelete(sqlite3*, Expr*);
|
||||
ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
|
||||
ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
|
||||
void sqlite3ExprListSetSortOrder(ExprList*,int);
|
||||
void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
|
||||
void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
|
||||
@@ -3701,8 +3706,8 @@ Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
|
||||
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
|
||||
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
|
||||
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
|
||||
void sqlite3Vacuum(Parse*);
|
||||
int sqlite3RunVacuum(char**, sqlite3*);
|
||||
void sqlite3Vacuum(Parse*,Token*);
|
||||
int sqlite3RunVacuum(char**, sqlite3*, int);
|
||||
char *sqlite3NameFromToken(sqlite3*, Token*);
|
||||
int sqlite3ExprCompare(Expr*, Expr*, int);
|
||||
int sqlite3ExprListCompare(ExprList*, ExprList*, int);
|
||||
@@ -3878,6 +3883,7 @@ const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
|
||||
void sqlite3TableAffinity(Vdbe*, Table*, int);
|
||||
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
|
||||
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
|
||||
char sqlite3TableColumnAffinity(Table*,int);
|
||||
char sqlite3ExprAffinity(Expr *pExpr);
|
||||
int sqlite3Atoi64(const char*, i64*, int, u8);
|
||||
int sqlite3DecOrHexToI64(const char*, i64*);
|
||||
@@ -3943,7 +3949,7 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
||||
int sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*);
|
||||
int sqlite3CodeSubselect(Parse *, Expr *, int, int);
|
||||
int sqlite3CodeSubselect(Parse*, Expr *, int, int);
|
||||
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
||||
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
@@ -3998,12 +4004,20 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
|
||||
void sqlite3BackupRestart(sqlite3_backup *);
|
||||
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
|
||||
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3ExprCheckIN(Parse*, Expr*);
|
||||
#else
|
||||
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
void sqlite3AnalyzeFunctions(void);
|
||||
int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
|
||||
int sqlite3Stat4ProbeSetValue(
|
||||
Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
|
||||
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord*);
|
||||
int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
|
||||
char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -4156,7 +4170,7 @@ const char *sqlite3JournalModename(int);
|
||||
#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
|
||||
#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
|
||||
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
|
||||
int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
|
||||
int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
|
||||
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
int sqlite3JournalSize(sqlite3_vfs *);
|
||||
@@ -4261,4 +4275,9 @@ int sqlite3ThreadJoin(SQLiteThread*, void**);
|
||||
int sqlite3DbstatRegister(sqlite3*);
|
||||
#endif
|
||||
|
||||
int sqlite3ExprVectorSize(Expr *pExpr);
|
||||
int sqlite3ExprIsVector(Expr *pExpr);
|
||||
Expr *sqlite3VectorFieldSubexpr(Expr*, int);
|
||||
Expr *sqlite3ExprForVectorField(Parse*,Expr*,int);
|
||||
|
||||
#endif /* SQLITEINT_H */
|
||||
|
||||
+24
@@ -7195,6 +7195,29 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the name of the main database schema from "main" to "icecube".
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_dbconfig_maindbname_icecube(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "icecube");
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
@@ -7328,6 +7351,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_enable_load_extension", test_enable_load, 0},
|
||||
{ "sqlite3_extended_result_codes", test_extended_result_codes, 0},
|
||||
{ "sqlite3_limit", test_limit, 0},
|
||||
{ "dbconfig_maindbname_icecube", test_dbconfig_maindbname_icecube },
|
||||
|
||||
{ "save_prng_state", save_prng_state, 0 },
|
||||
{ "restore_prng_state", restore_prng_state, 0 },
|
||||
|
||||
+6
-2
@@ -499,9 +499,13 @@ static int fstreeFilter(
|
||||
char aWild[2] = { '\0', '\0' };
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
zRoot = sqlite3_mprintf("%s%c", getenv("SystemDrive"), '/');
|
||||
const char *zDrive = windirent_getenv("fstreeDrive");
|
||||
if( zDrive==0 ){
|
||||
zDrive = windirent_getenv("SystemDrive");
|
||||
}
|
||||
zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
|
||||
nRoot = sqlite3Strlen30(zRoot);
|
||||
zPrefix = sqlite3_mprintf("%s", getenv("SystemDrive"));
|
||||
zPrefix = sqlite3_mprintf("%s", zDrive);
|
||||
nPrefix = sqlite3Strlen30(zPrefix);
|
||||
#else
|
||||
zRoot = "/";
|
||||
|
||||
+29
-1
@@ -17,6 +17,34 @@
|
||||
|
||||
#include "test_windirent.h"
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX getenv() function using the Win32 API.
|
||||
** This function is not thread-safe.
|
||||
*/
|
||||
const char *windirent_getenv(
|
||||
const char *name
|
||||
){
|
||||
static char value[32768]; /* Maximum length, per MSDN */
|
||||
DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
|
||||
DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
dwRet = GetEnvironmentVariableA(name, value, dwSize);
|
||||
if( dwRet==0 || dwRet>dwSize ){
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() failed -OR-
|
||||
** the buffer is not large enough. Either way, return NULL.
|
||||
*/
|
||||
return 0;
|
||||
}else{
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() succeeded
|
||||
** -AND- the buffer contains the entire value.
|
||||
*/
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX opendir() function using the MSVCRT.
|
||||
*/
|
||||
@@ -32,7 +60,7 @@ LPDIR opendir(
|
||||
|
||||
/* TODO: Remove this if Unix-style root paths are not used. */
|
||||
if( sqlite3_stricmp(dirname, "/")==0 ){
|
||||
dirname = getenv("SystemDrive");
|
||||
dirname = windirent_getenv("SystemDrive");
|
||||
}
|
||||
|
||||
_snprintf(data.name, namesize, "%s\\*", dirname);
|
||||
|
||||
@@ -92,6 +92,13 @@ struct DIR {
|
||||
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
||||
};
|
||||
|
||||
/*
|
||||
** Provide the function prototype for the POSIX compatiable getenv()
|
||||
** function. This function is not thread-safe.
|
||||
*/
|
||||
|
||||
extern const char *windirent_getenv(const char *name);
|
||||
|
||||
/*
|
||||
** Finally, we can provide the function prototypes for the opendir(),
|
||||
** readdir(), readdir_r(), and closedir() POSIX functions.
|
||||
|
||||
+19
-16
@@ -502,14 +502,26 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pParse->nVar==0 );
|
||||
assert( pParse->nzVar==0 );
|
||||
assert( pParse->azVar==0 );
|
||||
while( zSql[i]!=0 ){
|
||||
while( 1 ){
|
||||
assert( i>=0 );
|
||||
pParse->sLastToken.z = &zSql[i];
|
||||
pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
|
||||
i += pParse->sLastToken.n;
|
||||
if( i>mxSqlLen ){
|
||||
pParse->rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
if( zSql[i]!=0 ){
|
||||
pParse->sLastToken.z = &zSql[i];
|
||||
pParse->sLastToken.n = sqlite3GetToken((u8*)&zSql[i],&tokenType);
|
||||
i += pParse->sLastToken.n;
|
||||
if( i>mxSqlLen ){
|
||||
pParse->rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
/* Upon reaching the end of input, call the parser two more times
|
||||
** with tokens TK_SEMI and 0, in that order. */
|
||||
if( lastTokenParsed==TK_SEMI ){
|
||||
tokenType = 0;
|
||||
}else if( lastTokenParsed==0 ){
|
||||
break;
|
||||
}else{
|
||||
tokenType = TK_SEMI;
|
||||
}
|
||||
}
|
||||
if( tokenType>=TK_SPACE ){
|
||||
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
|
||||
@@ -530,15 +542,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
}
|
||||
assert( nErr==0 );
|
||||
pParse->zTail = &zSql[i];
|
||||
if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
assert( zSql[i]==0 );
|
||||
if( lastTokenParsed!=TK_SEMI ){
|
||||
sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
|
||||
}
|
||||
if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
|
||||
}
|
||||
}
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
sqlite3_mutex_enter(sqlite3MallocMutex());
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
|
||||
|
||||
+22
-5
@@ -120,7 +120,7 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
|
||||
|
||||
|
||||
/*
|
||||
** Generate a human-readable description of a the Select object.
|
||||
** Generate a human-readable description of a Select object.
|
||||
*/
|
||||
void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
int n = 0;
|
||||
@@ -451,6 +451,15 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
|
||||
break;
|
||||
}
|
||||
case TK_VECTOR: {
|
||||
sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
|
||||
break;
|
||||
}
|
||||
case TK_SELECT_COLUMN: {
|
||||
sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
|
||||
sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
|
||||
break;
|
||||
@@ -467,21 +476,20 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression list.
|
||||
*/
|
||||
void sqlite3TreeViewExprList(
|
||||
void sqlite3TreeViewBareExprList(
|
||||
TreeView *pView,
|
||||
const ExprList *pList,
|
||||
u8 moreToFollow,
|
||||
const char *zLabel
|
||||
){
|
||||
int i;
|
||||
pView = sqlite3TreeViewPush(pView, moreToFollow);
|
||||
if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
|
||||
if( pList==0 ){
|
||||
sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
|
||||
}else{
|
||||
int i;
|
||||
sqlite3TreeViewLine(pView, "%s", zLabel);
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
int j = pList->a[i].u.x.iOrderByCol;
|
||||
@@ -493,6 +501,15 @@ void sqlite3TreeViewExprList(
|
||||
if( j ) sqlite3TreeViewPop(pView);
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3TreeViewExprList(
|
||||
TreeView *pView,
|
||||
const ExprList *pList,
|
||||
u8 moreToFollow,
|
||||
const char *zLabel
|
||||
){
|
||||
pView = sqlite3TreeViewPush(pView, moreToFollow);
|
||||
sqlite3TreeViewBareExprList(pView, pList, zLabel);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -214,8 +214,8 @@ void sqlite3BeginTrigger(
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int code = SQLITE_CREATE_TRIGGER;
|
||||
const char *zDb = db->aDb[iTabDb].zName;
|
||||
const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
|
||||
const char *zDb = db->aDb[iTabDb].zDbSName;
|
||||
const char *zDbTrig = isTemp ? db->aDb[1].zDbSName : zDb;
|
||||
if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
|
||||
if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
|
||||
goto trigger_cleanup;
|
||||
@@ -309,7 +309,7 @@ void sqlite3FinishTrigger(
|
||||
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), zName,
|
||||
pTrig->table, z);
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@@ -498,7 +498,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
|
||||
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
|
||||
if( zDb && sqlite3StrICmp(db->aDb[j].zDbSName, zDb) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
|
||||
if( pTrigger ) break;
|
||||
@@ -544,7 +544,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int code = SQLITE_DROP_TRIGGER;
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;
|
||||
if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) ||
|
||||
@@ -560,7 +560,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
||||
if( (v = sqlite3GetVdbe(pParse))!=0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrigger->zName
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pTrigger->zName
|
||||
);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
|
||||
@@ -663,8 +663,10 @@ static SrcList *targetSrcList(
|
||||
pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget);
|
||||
iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema);
|
||||
if( iDb==0 || iDb>=2 ){
|
||||
const char *zDb;
|
||||
assert( iDb<db->nDb );
|
||||
pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb);
|
||||
}
|
||||
}
|
||||
return pSrc;
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ void sqlite3Update(
|
||||
int rc;
|
||||
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
|
||||
j<0 ? "ROWID" : pTab->aCol[j].zName,
|
||||
db->aDb[iDb].zName);
|
||||
db->aDb[iDb].zDbSName);
|
||||
if( rc==SQLITE_DENY ){
|
||||
goto update_cleanup;
|
||||
}else if( rc==SQLITE_IGNORE ){
|
||||
|
||||
+81
-105
@@ -18,57 +18,52 @@
|
||||
#include "vdbeInt.h"
|
||||
|
||||
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
|
||||
/*
|
||||
** Finalize a prepared statement. If there was an error, store the
|
||||
** text of the error message in *pzErrMsg. Return the result code.
|
||||
*/
|
||||
static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
|
||||
int rc;
|
||||
rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute zSql on database db. Return an error code.
|
||||
** Execute zSql on database db.
|
||||
**
|
||||
** If zSql returns rows, then each row will have exactly one
|
||||
** column. (This will only happen if zSql begins with "SELECT".)
|
||||
** Take each row of result and call execSql() again recursively.
|
||||
**
|
||||
** The execSqlF() routine does the same thing, except it accepts
|
||||
** a format string as its third argument
|
||||
*/
|
||||
static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
sqlite3_stmt *pStmt;
|
||||
VVA_ONLY( int rc; )
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
|
||||
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
|
||||
return sqlite3_errcode(db);
|
||||
}
|
||||
VVA_ONLY( rc = ) sqlite3_step(pStmt);
|
||||
assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
|
||||
return vacuumFinalize(db, pStmt, pzErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute zSql on database db. The statement returns exactly
|
||||
** one column. Execute this as SQL on the same database.
|
||||
*/
|
||||
static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
/* printf("SQL: [%s]\n", zSql); fflush(stdout); */
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
|
||||
if( rc!=SQLITE_OK ){
|
||||
vacuumFinalize(db, pStmt, pzErrMsg);
|
||||
return rc;
|
||||
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
||||
const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
|
||||
assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
|
||||
if( zSubSql ){
|
||||
assert( zSubSql[0]!='S' );
|
||||
rc = execSql(db, pzErrMsg, zSubSql);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
}
|
||||
|
||||
return vacuumFinalize(db, pStmt, pzErrMsg);
|
||||
assert( rc!=SQLITE_ROW );
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
|
||||
}
|
||||
(void)sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
|
||||
char *z;
|
||||
va_list ap;
|
||||
int rc;
|
||||
va_start(ap, zSql);
|
||||
z = sqlite3VMPrintf(db, zSql, ap);
|
||||
va_end(ap);
|
||||
if( z==0 ) return SQLITE_NOMEM;
|
||||
rc = execSql(db, pzErrMsg, z);
|
||||
sqlite3DbFree(db, z);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -101,11 +96,12 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
** transient would cause the database file to appear to be deleted
|
||||
** following reboot.
|
||||
*/
|
||||
void sqlite3Vacuum(Parse *pParse){
|
||||
void sqlite3Vacuum(Parse *pParse, Token *pNm){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
|
||||
sqlite3VdbeUsesBtree(v, 0);
|
||||
int iDb = pNm ? sqlite3TwoPartName(pParse, pNm, pNm, &pNm) : 0;
|
||||
if( v && (iDb>=2 || iDb==0) ){
|
||||
sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -113,11 +109,10 @@ void sqlite3Vacuum(Parse *pParse){
|
||||
/*
|
||||
** This routine implements the OP_Vacuum opcode of the VDBE.
|
||||
*/
|
||||
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
Btree *pMain; /* The database being vacuumed */
|
||||
Btree *pTemp; /* The temporary database we vacuum into */
|
||||
char *zSql = 0; /* SQL statements */
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
int saved_nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
||||
@@ -126,6 +121,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
int isMemDb; /* True if vacuuming a :memory: database */
|
||||
int nRes; /* Bytes of reserved space at the end of each page */
|
||||
int nDb; /* Number of attached databases */
|
||||
const char *zDbMain; /* Schema name of database to vacuum */
|
||||
|
||||
if( !db->autoCommit ){
|
||||
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
|
||||
@@ -143,11 +139,13 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_mTrace = db->mTrace;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
|
||||
db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
|
||||
| SQLITE_PreferBuiltin | SQLITE_Vacuum);
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
|
||||
db->mTrace = 0;
|
||||
|
||||
pMain = db->aDb[0].pBt;
|
||||
zDbMain = db->aDb[iDb].zDbSName;
|
||||
pMain = db->aDb[iDb].pBt;
|
||||
isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
|
||||
|
||||
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma
|
||||
@@ -165,18 +163,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
** to write the journal header file.
|
||||
*/
|
||||
nDb = db->nDb;
|
||||
if( sqlite3TempInMemory(db) ){
|
||||
zSql = "ATTACH ':memory:' AS vacuum_db;";
|
||||
}else{
|
||||
zSql = "ATTACH '' AS vacuum_db;";
|
||||
}
|
||||
rc = execSql(db, pzErrMsg, zSql);
|
||||
if( db->nDb>nDb ){
|
||||
pDb = &db->aDb[db->nDb-1];
|
||||
assert( strcmp(pDb->zName,"vacuum_db")==0 );
|
||||
}
|
||||
rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
pTemp = db->aDb[db->nDb-1].pBt;
|
||||
assert( (db->nDb-1)==nDb );
|
||||
pDb = &db->aDb[nDb];
|
||||
assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
|
||||
pTemp = pDb->pBt;
|
||||
|
||||
/* The call to execSql() to attach the temp database has left the file
|
||||
** locked (as there was more than one active statement when the transaction
|
||||
@@ -197,16 +189,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3BtreeSetCacheSize(pTemp, db->aDb[0].pSchema->cache_size);
|
||||
sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
|
||||
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
|
||||
rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF);
|
||||
|
||||
/* Begin a transaction and take an exclusive lock on the main database
|
||||
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
|
||||
** to ensure that we do not try to change the page-size on a WAL database.
|
||||
*/
|
||||
rc = execSql(db, pzErrMsg, "BEGIN;");
|
||||
rc = execSql(db, pzErrMsg, "BEGIN");
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = sqlite3BtreeBeginTrans(pMain, 2);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
@@ -233,64 +224,48 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
/* Query the schema of the main database. Create a mirror schema
|
||||
** in the temporary database.
|
||||
*/
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
|
||||
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
|
||||
" AND coalesce(rootpage,1)>0"
|
||||
db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
|
||||
rc = execSqlF(db, pzErrMsg,
|
||||
"SELECT sql FROM \"%w\".sqlite_master"
|
||||
" WHERE type='table'AND name<>'sqlite_sequence'"
|
||||
" AND coalesce(rootpage,1)>0",
|
||||
zDbMain
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
|
||||
" FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
|
||||
" FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
|
||||
rc = execSqlF(db, pzErrMsg,
|
||||
"SELECT sql FROM \"%w\".sqlite_master"
|
||||
" WHERE type='index' AND length(sql)>10",
|
||||
zDbMain
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
db->init.iDb = 0;
|
||||
|
||||
/* Loop through the tables in the main database. For each, do
|
||||
** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
|
||||
** the contents to the temporary database.
|
||||
*/
|
||||
assert( (db->flags & SQLITE_Vacuum)==0 );
|
||||
db->flags |= SQLITE_Vacuum;
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'INSERT INTO vacuum_db.' || quote(name) "
|
||||
"|| ' SELECT * FROM main.' || quote(name) || ';'"
|
||||
"FROM main.sqlite_master "
|
||||
"WHERE type = 'table' AND name!='sqlite_sequence' "
|
||||
" AND coalesce(rootpage,1)>0"
|
||||
rc = execSqlF(db, pzErrMsg,
|
||||
"SELECT'INSERT INTO vacuum_db.'||quote(name)"
|
||||
"||' SELECT*FROM\"%w\".'||quote(name)"
|
||||
"FROM vacuum_db.sqlite_master "
|
||||
"WHERE type='table'AND coalesce(rootpage,1)>0",
|
||||
zDbMain
|
||||
);
|
||||
assert( (db->flags & SQLITE_Vacuum)!=0 );
|
||||
db->flags &= ~SQLITE_Vacuum;
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
/* Copy over the sequence table
|
||||
*/
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
|
||||
"FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'INSERT INTO vacuum_db.' || quote(name) "
|
||||
"|| ' SELECT * FROM main.' || quote(name) || ';' "
|
||||
"FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
|
||||
/* Copy the triggers, views, and virtual tables from the main database
|
||||
** over to the temporary database. None of these objects has any
|
||||
** associated storage, so all we have to do is copy their entries
|
||||
** from the SQLITE_MASTER table.
|
||||
*/
|
||||
rc = execSql(db, pzErrMsg,
|
||||
"INSERT INTO vacuum_db.sqlite_master "
|
||||
" SELECT type, name, tbl_name, rootpage, sql"
|
||||
" FROM main.sqlite_master"
|
||||
" WHERE type='view' OR type='trigger'"
|
||||
" OR (type='table' AND rootpage=0)"
|
||||
rc = execSqlF(db, pzErrMsg,
|
||||
"INSERT INTO vacuum_db.sqlite_master"
|
||||
" SELECT*FROM \"%w\".sqlite_master"
|
||||
" WHERE type IN('view','trigger')"
|
||||
" OR(type='table'AND rootpage=0)",
|
||||
zDbMain
|
||||
);
|
||||
if( rc ) goto end_of_vacuum;
|
||||
|
||||
@@ -344,6 +319,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
|
||||
end_of_vacuum:
|
||||
/* Restore the original value of db->flags */
|
||||
db->init.iDb = 0;
|
||||
db->flags = saved_flags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
|
||||
+135
-86
@@ -573,7 +573,7 @@ int sqlite3VdbeExec(
|
||||
sqlite3 *db = p->db; /* The database */
|
||||
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
|
||||
u8 encoding = ENC(db); /* The database encoding */
|
||||
int iCompare = 0; /* Result of last OP_Compare operation */
|
||||
int iCompare = 0; /* Result of last comparison */
|
||||
unsigned nVmStep = 0; /* Number of virtual machine steps */
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
|
||||
@@ -905,7 +905,7 @@ case OP_Yield: { /* in1, jump */
|
||||
}
|
||||
|
||||
/* Opcode: HaltIfNull P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P3]=null halt
|
||||
** Synopsis: if r[P3]=null halt
|
||||
**
|
||||
** Check the value in register P3. If it is NULL then Halt using
|
||||
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
|
||||
@@ -1118,7 +1118,7 @@ case OP_String: { /* out2 */
|
||||
}
|
||||
|
||||
/* Opcode: Null P1 P2 P3 * *
|
||||
** Synopsis: r[P2..P3]=NULL
|
||||
** Synopsis: r[P2..P3]=NULL
|
||||
**
|
||||
** Write a NULL into registers P2. If P3 greater than P2, then also write
|
||||
** NULL into register P3 and every register in between P2 and P3. If P3
|
||||
@@ -1147,7 +1147,7 @@ case OP_Null: { /* out2 */
|
||||
}
|
||||
|
||||
/* Opcode: SoftNull P1 * * * *
|
||||
** Synopsis: r[P1]=NULL
|
||||
** Synopsis: r[P1]=NULL
|
||||
**
|
||||
** Set register P1 to have the value NULL as seen by the OP_MakeRecord
|
||||
** instruction, but do not free any string or blob memory associated with
|
||||
@@ -1200,7 +1200,7 @@ case OP_Variable: { /* out2 */
|
||||
}
|
||||
|
||||
/* Opcode: Move P1 P2 P3 * *
|
||||
** Synopsis: r[P2@P3]=r[P1@P3]
|
||||
** Synopsis: r[P2@P3]=r[P1@P3]
|
||||
**
|
||||
** Move the P3 values in register P1..P1+P3-1 over into
|
||||
** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
|
||||
@@ -1310,7 +1310,7 @@ case OP_IntCopy: { /* out2 */
|
||||
}
|
||||
|
||||
/* Opcode: ResultRow P1 P2 * * *
|
||||
** Synopsis: output=r[P1@P2]
|
||||
** Synopsis: output=r[P1@P2]
|
||||
**
|
||||
** The registers P1 through P1+P2-1 contain a single row of
|
||||
** results. This opcode causes the sqlite3_step() call to terminate
|
||||
@@ -1443,14 +1443,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
|
||||
}
|
||||
|
||||
/* Opcode: Add P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P1]+r[P2]
|
||||
** Synopsis: r[P3]=r[P1]+r[P2]
|
||||
**
|
||||
** Add the value in register P1 to the value in register P2
|
||||
** and store the result in register P3.
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Multiply P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P1]*r[P2]
|
||||
** Synopsis: r[P3]=r[P1]*r[P2]
|
||||
**
|
||||
**
|
||||
** Multiply the value in register P1 by the value in register P2
|
||||
@@ -1458,14 +1458,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Subtract P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]-r[P1]
|
||||
** Synopsis: r[P3]=r[P2]-r[P1]
|
||||
**
|
||||
** Subtract the value in register P1 from the value in register P2
|
||||
** and store the result in register P3.
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Divide P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]/r[P1]
|
||||
** Synopsis: r[P3]=r[P2]/r[P1]
|
||||
**
|
||||
** Divide the value in register P1 by the value in register P2
|
||||
** and store the result in register P3 (P3=P2/P1). If the value in
|
||||
@@ -1473,7 +1473,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
|
||||
** NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Remainder P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]%r[P1]
|
||||
** Synopsis: r[P3]=r[P2]%r[P1]
|
||||
**
|
||||
** Compute the remainder after integer register P2 is divided by
|
||||
** register P1 and store the result in register P3.
|
||||
@@ -1706,21 +1706,21 @@ case OP_Function: {
|
||||
}
|
||||
|
||||
/* Opcode: BitAnd P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P1]&r[P2]
|
||||
** Synopsis: r[P3]=r[P1]&r[P2]
|
||||
**
|
||||
** Take the bit-wise AND of the values in register P1 and P2 and
|
||||
** store the result in register P3.
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: BitOr P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P1]|r[P2]
|
||||
** Synopsis: r[P3]=r[P1]|r[P2]
|
||||
**
|
||||
** Take the bit-wise OR of the values in register P1 and P2 and
|
||||
** store the result in register P3.
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: ShiftLeft P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]<<r[P1]
|
||||
** Synopsis: r[P3]=r[P2]<<r[P1]
|
||||
**
|
||||
** Shift the integer value in register P2 to the left by the
|
||||
** number of bits specified by the integer in register P1.
|
||||
@@ -1728,7 +1728,7 @@ case OP_Function: {
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: ShiftRight P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]>>r[P1]
|
||||
** Synopsis: r[P3]=r[P2]>>r[P1]
|
||||
**
|
||||
** Shift the integer value in register P2 to the right by the
|
||||
** number of bits specified by the integer in register P1.
|
||||
@@ -1788,7 +1788,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
|
||||
}
|
||||
|
||||
/* Opcode: AddImm P1 P2 * * *
|
||||
** Synopsis: r[P1]=r[P1]+P2
|
||||
** Synopsis: r[P1]=r[P1]+P2
|
||||
**
|
||||
** Add the constant P2 to the value in register P1.
|
||||
** The result is always an integer.
|
||||
@@ -1880,14 +1880,61 @@ case OP_Cast: { /* in1 */
|
||||
}
|
||||
#endif /* SQLITE_OMIT_CAST */
|
||||
|
||||
/* Opcode: Eq P1 P2 P3 P4 P5
|
||||
** Synopsis: IF r[P3]==r[P1]
|
||||
**
|
||||
** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
|
||||
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
|
||||
** store the result of comparison in register P2.
|
||||
**
|
||||
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
|
||||
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
|
||||
** to coerce both inputs according to this affinity before the
|
||||
** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
|
||||
** affinity is used. Note that the affinity conversions are stored
|
||||
** back into the input registers P1 and P3. So this opcode can cause
|
||||
** persistent changes to registers P1 and P3.
|
||||
**
|
||||
** Once any conversions have taken place, and neither value is NULL,
|
||||
** the values are compared. If both values are blobs then memcmp() is
|
||||
** used to determine the results of the comparison. If both values
|
||||
** are text, then the appropriate collating function specified in
|
||||
** P4 is used to do the comparison. If P4 is not specified then
|
||||
** memcmp() is used to compare text string. If both values are
|
||||
** numeric, then a numeric comparison is used. If the two values
|
||||
** are of different types, then numbers are considered less than
|
||||
** strings and strings are considered less than blobs.
|
||||
**
|
||||
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
|
||||
** true or false and is never NULL. If both operands are NULL then the result
|
||||
** of comparison is true. If either operand is NULL then the result is false.
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
**
|
||||
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
|
||||
** content of r[P2] is only changed if the new value is NULL or 0 (false).
|
||||
** In other words, a prior r[P2] value will not be overwritten by 1 (true).
|
||||
*/
|
||||
/* Opcode: Ne P1 P2 P3 P4 P5
|
||||
** Synopsis: IF r[P3]!=r[P1]
|
||||
**
|
||||
** This works just like the Eq opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are not equal. See the Eq opcode for
|
||||
** additional information.
|
||||
**
|
||||
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
|
||||
** content of r[P2] is only changed if the new value is NULL or 1 (true).
|
||||
** In other words, a prior r[P2] value will not be overwritten by 0 (false).
|
||||
*/
|
||||
/* Opcode: Lt P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]<r[P3] goto P2
|
||||
** Synopsis: IF r[P3]<r[P1]
|
||||
**
|
||||
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
|
||||
** jump to address P2.
|
||||
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
|
||||
** the result of comparison (0 or 1 or NULL) into register P2.
|
||||
**
|
||||
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
|
||||
** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
|
||||
** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
|
||||
** bit is clear then fall through if either operand is NULL.
|
||||
**
|
||||
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
|
||||
@@ -1907,56 +1954,23 @@ case OP_Cast: { /* in1 */
|
||||
** numeric, then a numeric comparison is used. If the two values
|
||||
** are of different types, then numbers are considered less than
|
||||
** strings and strings are considered less than blobs.
|
||||
**
|
||||
** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
|
||||
** store a boolean result (either 0, or 1, or NULL) in register P2.
|
||||
**
|
||||
** If the SQLITE_NULLEQ bit is set in P5, then NULL values are considered
|
||||
** equal to one another, provided that they do not have their MEM_Cleared
|
||||
** bit set.
|
||||
*/
|
||||
/* Opcode: Ne P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]!=r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are not equal. See the Lt opcode for
|
||||
** additional information.
|
||||
**
|
||||
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
|
||||
** true or false and is never NULL. If both operands are NULL then the result
|
||||
** of comparison is false. If either operand is NULL then the result is true.
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Eq P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]==r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are equal.
|
||||
** See the Lt opcode for additional information.
|
||||
**
|
||||
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
|
||||
** true or false and is never NULL. If both operands are NULL then the result
|
||||
** of comparison is true. If either operand is NULL then the result is false.
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Le P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]<=r[P3] goto P2
|
||||
** Synopsis: IF r[P3]<=r[P1]
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is less than or equal to the content of
|
||||
** register P1. See the Lt opcode for additional information.
|
||||
*/
|
||||
/* Opcode: Gt P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]>r[P3] goto P2
|
||||
** Synopsis: IF r[P3]>r[P1]
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is greater than the content of
|
||||
** register P1. See the Lt opcode for additional information.
|
||||
*/
|
||||
/* Opcode: Ge P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P1]>=r[P3] goto P2
|
||||
** Synopsis: IF r[P3]>=r[P1]
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is greater than or equal to the content of
|
||||
@@ -1968,7 +1982,7 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
|
||||
case OP_Le: /* same as TK_LE, jump, in1, in3 */
|
||||
case OP_Gt: /* same as TK_GT, jump, in1, in3 */
|
||||
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
int res; /* Result of the comparison of pIn1 against pIn3 */
|
||||
int res, res2; /* Result of the comparison of pIn1 against pIn3 */
|
||||
char affinity; /* Affinity to use for comparison */
|
||||
u16 flags1; /* Copy of initial value of pIn1->flags */
|
||||
u16 flags3; /* Copy of initial value of pIn3->flags */
|
||||
@@ -1991,9 +2005,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
&& (flags3&MEM_Null)!=0
|
||||
&& (flags3&MEM_Cleared)==0
|
||||
){
|
||||
res = 0; /* Results are equal */
|
||||
res = 0; /* Operands are equal */
|
||||
}else{
|
||||
res = 1; /* Results are not equal */
|
||||
res = 1; /* Operands are not equal */
|
||||
}
|
||||
}else{
|
||||
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
|
||||
@@ -2002,6 +2016,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
*/
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
iCompare = 1; /* Operands are not equal */
|
||||
memAboutToChange(p, pOut);
|
||||
MemSetTypeFlag(pOut, MEM_Null);
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
@@ -2055,12 +2070,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
|
||||
}
|
||||
switch( pOp->opcode ){
|
||||
case OP_Eq: res = res==0; break;
|
||||
case OP_Ne: res = res!=0; break;
|
||||
case OP_Lt: res = res<0; break;
|
||||
case OP_Le: res = res<=0; break;
|
||||
case OP_Gt: res = res>0; break;
|
||||
default: res = res>=0; break;
|
||||
case OP_Eq: res2 = res==0; break;
|
||||
case OP_Ne: res2 = res; break;
|
||||
case OP_Lt: res2 = res<0; break;
|
||||
case OP_Le: res2 = res<=0; break;
|
||||
case OP_Gt: res2 = res>0; break;
|
||||
default: res2 = res>=0; break;
|
||||
}
|
||||
|
||||
/* Undo any changes made by applyAffinity() to the input registers. */
|
||||
@@ -2071,19 +2086,55 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
iCompare = res;
|
||||
res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
|
||||
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
|
||||
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
|
||||
** and prevents OP_Ne from overwriting NULL with 0. This flag
|
||||
** is only used in contexts where either:
|
||||
** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
|
||||
** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
|
||||
** Therefore it is not necessary to check the content of r[P2] for
|
||||
** NULL. */
|
||||
assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
|
||||
assert( res2==0 || res2==1 );
|
||||
testcase( res2==0 && pOp->opcode==OP_Eq );
|
||||
testcase( res2==1 && pOp->opcode==OP_Eq );
|
||||
testcase( res2==0 && pOp->opcode==OP_Ne );
|
||||
testcase( res2==1 && pOp->opcode==OP_Ne );
|
||||
if( (pOp->opcode==OP_Eq)==res2 ) break;
|
||||
}
|
||||
memAboutToChange(p, pOut);
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
pOut->u.i = res;
|
||||
pOut->u.i = res2;
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
}else{
|
||||
VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
|
||||
if( res ){
|
||||
if( res2 ){
|
||||
goto jump_to_p2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ElseNotEq * P2 * * *
|
||||
**
|
||||
** This opcode must immediately follow an Lt or Gt comparison operator.
|
||||
** If the operands in that previous comparison had been used with an Eq
|
||||
** operator and if the result of that Eq would be NULL or false (0), then
|
||||
** then jump to P2. If the result of comparing the two previous operands
|
||||
** using Eq would have been true (1), then fall through.
|
||||
*/
|
||||
case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
|
||||
assert( pOp>aOp );
|
||||
assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
|
||||
assert( pOp[-1].p5 & SQLITE_STOREP2 );
|
||||
VdbeBranchTaken(iCompare!=0, 2);
|
||||
if( iCompare!=0 ) goto jump_to_p2;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Permutation * * * P4 *
|
||||
**
|
||||
** Set the permutation used by the OP_Compare operator to be the array
|
||||
@@ -2333,7 +2384,7 @@ case OP_IfNot: { /* jump, in1 */
|
||||
}
|
||||
|
||||
/* Opcode: IsNull P1 P2 * * *
|
||||
** Synopsis: if r[P1]==NULL goto P2
|
||||
** Synopsis: if r[P1]==NULL goto P2
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is NULL.
|
||||
*/
|
||||
@@ -2361,7 +2412,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
|
||||
}
|
||||
|
||||
/* Opcode: Column P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P3]=PX
|
||||
** Synopsis: r[P3]=PX
|
||||
**
|
||||
** Interpret the data that cursor P1 points to as a structure built using
|
||||
** the MakeRecord instruction. (See the MakeRecord opcode for additional
|
||||
@@ -3871,7 +3922,6 @@ seek_not_found:
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Found P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
@@ -4304,7 +4354,7 @@ case OP_NewRowid: { /* out2 */
|
||||
** for indices is OP_IdxInsert.
|
||||
*/
|
||||
/* Opcode: InsertInt P1 P2 P3 P4 P5
|
||||
** Synopsis: intkey=P3 data=r[P2]
|
||||
** Synopsis: intkey=P3 data=r[P2]
|
||||
**
|
||||
** This works exactly like OP_Insert except that the key is the
|
||||
** integer value P3, not the value of the integer stored in register P3.
|
||||
@@ -4346,7 +4396,7 @@ case OP_InsertInt: {
|
||||
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
|
||||
assert( pC->isTable );
|
||||
assert( pC->iDb>=0 );
|
||||
zDb = db->aDb[pC->iDb].zName;
|
||||
zDb = db->aDb[pC->iDb].zDbSName;
|
||||
pTab = pOp->p4.pTab;
|
||||
assert( HasRowid(pTab) );
|
||||
op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
|
||||
@@ -4463,7 +4513,7 @@ case OP_Delete: {
|
||||
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
|
||||
assert( pC->iDb>=0 );
|
||||
assert( pOp->p4.pTab!=0 );
|
||||
zDb = db->aDb[pC->iDb].zName;
|
||||
zDb = db->aDb[pC->iDb].zDbSName;
|
||||
pTab = pOp->p4.pTab;
|
||||
if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
|
||||
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
|
||||
@@ -4535,7 +4585,7 @@ case OP_ResetCount: {
|
||||
}
|
||||
|
||||
/* Opcode: SorterCompare P1 P2 P3 P4
|
||||
** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
|
||||
** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
|
||||
**
|
||||
** P1 is a sorter cursor. This instruction compares a prefix of the
|
||||
** record blob in register P3 against a prefix of the entry that
|
||||
@@ -5062,7 +5112,7 @@ case OP_IdxDelete: {
|
||||
}
|
||||
|
||||
/* Opcode: Seek P1 * P3 P4 *
|
||||
** Synopsis: Move P3 to P1.rowid
|
||||
** Synopsis: Move P3 to P1.rowid
|
||||
**
|
||||
** P1 is an open index cursor and P3 is a cursor on the corresponding
|
||||
** table. This opcode does a deferred seek of the P3 table cursor
|
||||
@@ -5433,7 +5483,7 @@ case OP_ParseSchema: {
|
||||
initData.pzErrMsg = &p->zErrMsg;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
|
||||
db->aDb[iDb].zName, zMaster, pOp->p4.z);
|
||||
db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
@@ -5569,7 +5619,7 @@ case OP_IntegrityCk: {
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
|
||||
/* Opcode: RowSetAdd P1 P2 * * *
|
||||
** Synopsis: rowset(P1)=r[P2]
|
||||
** Synopsis: rowset(P1)=r[P2]
|
||||
**
|
||||
** Insert the integer value held by register P2 into a boolean index
|
||||
** held in register P1.
|
||||
@@ -5589,7 +5639,7 @@ case OP_RowSetAdd: { /* in1, in2 */
|
||||
}
|
||||
|
||||
/* Opcode: RowSetRead P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=rowset(P1)
|
||||
** Synopsis: r[P3]=rowset(P1)
|
||||
**
|
||||
** Extract the smallest value from boolean index P1 and put that value into
|
||||
** register P3. Or, if boolean index P1 is initially empty, leave P3
|
||||
@@ -6262,15 +6312,14 @@ case OP_JournalMode: { /* out2 */
|
||||
#endif /* SQLITE_OMIT_PRAGMA */
|
||||
|
||||
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
|
||||
/* Opcode: Vacuum * * * * *
|
||||
/* Opcode: Vacuum P1 * * * *
|
||||
**
|
||||
** Vacuum the entire database. This opcode will cause other virtual
|
||||
** machines to be created and run. It may not be called from within
|
||||
** a transaction.
|
||||
** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
|
||||
** for an attached database. The "temp" database may not be vacuumed.
|
||||
*/
|
||||
case OP_Vacuum: {
|
||||
assert( p->readOnly==0 );
|
||||
rc = sqlite3RunVacuum(&p->zErrMsg, db);
|
||||
rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
break;
|
||||
}
|
||||
@@ -6769,7 +6818,7 @@ case OP_MaxPgcnt: { /* out2 */
|
||||
|
||||
|
||||
/* Opcode: Init * P2 * P4 *
|
||||
** Synopsis: Start at P2
|
||||
** Synopsis: Start at P2
|
||||
**
|
||||
** Programs contain a single instance of this opcode as the very first
|
||||
** opcode.
|
||||
@@ -6817,7 +6866,7 @@ case OP_Init: { /* jump */
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( DbMaskTest(p->btreeMask, i)==0 ) continue;
|
||||
sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace);
|
||||
sqlite3_file_control(db, db->aDb[i].zDbSName, SQLITE_FCNTL_TRACE, zTrace);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_USE_FCNTL_TRACE */
|
||||
@@ -6829,8 +6878,8 @@ case OP_Init: { /* jump */
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
if( pOp->p2 ) goto jump_to_p2;
|
||||
break;
|
||||
assert( pOp->p2>0 );
|
||||
goto jump_to_p2;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_CURSOR_HINTS
|
||||
|
||||
+1
-1
@@ -499,7 +499,7 @@ static int doWalCallbacks(sqlite3 *db){
|
||||
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
|
||||
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
|
||||
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1094,12 +1094,21 @@ static int displayComment(
|
||||
const char *zSynopsis;
|
||||
int nOpName;
|
||||
int ii, jj;
|
||||
char zAlt[50];
|
||||
zOpName = sqlite3OpcodeName(pOp->opcode);
|
||||
nOpName = sqlite3Strlen30(zOpName);
|
||||
if( zOpName[nOpName+1] ){
|
||||
int seenCom = 0;
|
||||
char c;
|
||||
zSynopsis = zOpName += nOpName + 1;
|
||||
if( strncmp(zSynopsis,"IF ",3)==0 ){
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
|
||||
}
|
||||
zSynopsis = zAlt;
|
||||
}
|
||||
for(ii=jj=0; jj<nTemp-1 && (c = zSynopsis[ii])!=0; ii++){
|
||||
if( c=='P' ){
|
||||
c = zSynopsis[++ii];
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ int sqlite3_blob_open(
|
||||
goto blob_open_out;
|
||||
}
|
||||
pBlob->pTab = pTab;
|
||||
pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zName;
|
||||
pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
||||
|
||||
/* Now search pTab for the exact column. */
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++) {
|
||||
|
||||
+34
-19
@@ -1520,9 +1520,9 @@ static int stat4ValueFromExpr(
|
||||
** structures intended to be compared against sample index keys stored
|
||||
** in the sqlite_stat4 table.
|
||||
**
|
||||
** A single call to this function attempts to populates field iVal (leftmost
|
||||
** is 0 etc.) of the unpacked record with a value extracted from expression
|
||||
** pExpr. Extraction of values is possible if:
|
||||
** A single call to this function populates zero or more fields of the
|
||||
** record starting with field iVal (fields are numbered from left to
|
||||
** right starting with 0). A single field is populated if:
|
||||
**
|
||||
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
|
||||
**
|
||||
@@ -1531,10 +1531,14 @@ static int stat4ValueFromExpr(
|
||||
** * The sqlite3ValueFromExpr() function is able to extract a value
|
||||
** from the expression (i.e. the expression is a literal value).
|
||||
**
|
||||
** If a value can be extracted, the affinity passed as the 5th argument
|
||||
** is applied to it before it is copied into the UnpackedRecord. Output
|
||||
** parameter *pbOk is set to true if a value is extracted, or false
|
||||
** otherwise.
|
||||
** Or, if pExpr is a TK_VECTOR, one field is populated for each of the
|
||||
** vector components that match either of the two latter criteria listed
|
||||
** above.
|
||||
**
|
||||
** Before any value is appended to the record, the affinity of the
|
||||
** corresponding column within index pIdx is applied to it. Before
|
||||
** this function returns, output parameter *pnExtract is set to the
|
||||
** number of values appended to the record.
|
||||
**
|
||||
** When this function is called, *ppRec must either point to an object
|
||||
** allocated by an earlier call to this function, or must be NULL. If it
|
||||
@@ -1550,22 +1554,33 @@ int sqlite3Stat4ProbeSetValue(
|
||||
Index *pIdx, /* Index being probed */
|
||||
UnpackedRecord **ppRec, /* IN/OUT: Probe record */
|
||||
Expr *pExpr, /* The expression to extract a value from */
|
||||
u8 affinity, /* Affinity to use */
|
||||
int nElem, /* Maximum number of values to append */
|
||||
int iVal, /* Array element to populate */
|
||||
int *pbOk /* OUT: True if value was extracted */
|
||||
int *pnExtract /* OUT: Values appended to the record */
|
||||
){
|
||||
int rc;
|
||||
sqlite3_value *pVal = 0;
|
||||
struct ValueNewStat4Ctx alloc;
|
||||
int rc = SQLITE_OK;
|
||||
int nExtract = 0;
|
||||
|
||||
alloc.pParse = pParse;
|
||||
alloc.pIdx = pIdx;
|
||||
alloc.ppRec = ppRec;
|
||||
alloc.iVal = iVal;
|
||||
if( pExpr==0 || pExpr->op!=TK_SELECT ){
|
||||
int i;
|
||||
struct ValueNewStat4Ctx alloc;
|
||||
|
||||
rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
|
||||
assert( pVal==0 || pVal->db==pParse->db );
|
||||
*pbOk = (pVal!=0);
|
||||
alloc.pParse = pParse;
|
||||
alloc.pIdx = pIdx;
|
||||
alloc.ppRec = ppRec;
|
||||
|
||||
for(i=0; i<nElem; i++){
|
||||
sqlite3_value *pVal = 0;
|
||||
Expr *pElem = (pExpr ? sqlite3VectorFieldSubexpr(pExpr, i) : 0);
|
||||
u8 aff = sqlite3IndexColumnAffinity(pParse->db, pIdx, iVal+i);
|
||||
alloc.iVal = iVal+i;
|
||||
rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal);
|
||||
if( !pVal ) break;
|
||||
nExtract++;
|
||||
}
|
||||
}
|
||||
|
||||
*pnExtract = nExtract;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -344,7 +344,7 @@ void sqlite3VtabBeginParse(
|
||||
*/
|
||||
if( pTable->azModuleArg ){
|
||||
sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
|
||||
pTable->azModuleArg[0], pParse->db->aDb[iDb].zName);
|
||||
pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -408,7 +408,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
"UPDATE %Q.%s "
|
||||
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
|
||||
"WHERE rowid=#%d",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
pTab->zName,
|
||||
pTab->zName,
|
||||
zStmt,
|
||||
@@ -518,7 +518,7 @@ static int vtabCallConstructor(
|
||||
pVTable->pMod = pMod;
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
pTab->azModuleArg[1] = db->aDb[iDb].zName;
|
||||
pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
|
||||
|
||||
/* Invoke the virtual table constructor */
|
||||
assert( &db->pVtabCtx );
|
||||
@@ -672,7 +672,7 @@ static void addToVTrans(sqlite3 *db, VTable *pVTab){
|
||||
** This function is invoked by the vdbe to call the xCreate method
|
||||
** of the virtual table named zTab in database iDb.
|
||||
**
|
||||
** If an error occurs, *pzErr is set to point an an English language
|
||||
** If an error occurs, *pzErr is set to point to an English language
|
||||
** description of the error and an SQLITE_XXX error code is returned.
|
||||
** In this case the caller must call sqlite3DbFree(db, ) on *pzErr.
|
||||
*/
|
||||
@@ -682,7 +682,7 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
|
||||
Module *pMod;
|
||||
const char *zMod;
|
||||
|
||||
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
|
||||
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
|
||||
assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable );
|
||||
|
||||
/* Locate the required virtual table module */
|
||||
@@ -806,7 +806,7 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
|
||||
int rc = SQLITE_OK;
|
||||
Table *pTab;
|
||||
|
||||
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
|
||||
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
|
||||
if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){
|
||||
VTable *p;
|
||||
int (*xDestroy)(sqlite3_vtab *);
|
||||
|
||||
+8
-10
@@ -41,17 +41,15 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
|
||||
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
|
||||
testcase( ExprHasProperty(pExpr, EP_Reduced) );
|
||||
rc = pWalker->xExprCallback(pWalker, pExpr);
|
||||
if( rc==WRC_Continue
|
||||
&& !ExprHasProperty(pExpr,EP_TokenOnly) ){
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else{
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
}
|
||||
if( rc || ExprHasProperty(pExpr,EP_TokenOnly) ) return rc & WRC_Abort;
|
||||
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
if( pExpr->pRight && walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else{
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
}
|
||||
return rc & WRC_Abort;
|
||||
return WRC_Continue;
|
||||
}
|
||||
int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
|
||||
return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue;
|
||||
|
||||
+195
-44
@@ -826,7 +826,8 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
WhereClause *pWC,
|
||||
Bitmask mUnusable, /* Ignore terms with these prereqs */
|
||||
struct SrcList_item *pSrc,
|
||||
ExprList *pOrderBy
|
||||
ExprList *pOrderBy,
|
||||
u16 *pmNoOmit /* Mask of terms not to omit */
|
||||
){
|
||||
int i, j;
|
||||
int nTerm;
|
||||
@@ -836,6 +837,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
WhereTerm *pTerm;
|
||||
int nOrderBy;
|
||||
sqlite3_index_info *pIdxInfo;
|
||||
u16 mNoOmit = 0;
|
||||
|
||||
/* Count the number of possible WHERE clause constraints referring
|
||||
** to this virtual table */
|
||||
@@ -924,6 +926,15 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
||||
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
|
||||
|
||||
if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
|
||||
&& sqlite3ExprIsVector(pTerm->pExpr->pRight)
|
||||
){
|
||||
if( i<16 ) mNoOmit |= (1 << i);
|
||||
if( op==WO_LT ) pIdxCons[j].op = WO_LE;
|
||||
if( op==WO_GT ) pIdxCons[j].op = WO_GE;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
for(i=0; i<nOrderBy; i++){
|
||||
@@ -932,6 +943,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
|
||||
}
|
||||
|
||||
*pmNoOmit = mNoOmit;
|
||||
return pIdxInfo;
|
||||
}
|
||||
|
||||
@@ -1207,7 +1219,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
|
||||
/*
|
||||
** Return the affinity for a single column of an index.
|
||||
*/
|
||||
static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
|
||||
char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
|
||||
assert( iCol>=0 && iCol<pIdx->nColumn );
|
||||
if( !pIdx->zColAff ){
|
||||
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
|
||||
@@ -1384,7 +1396,8 @@ static int whereRangeScanEst(
|
||||
if( nEq==pBuilder->nRecValid ){
|
||||
UnpackedRecord *pRec = pBuilder->pRec;
|
||||
tRowcnt a[2];
|
||||
u8 aff;
|
||||
int nBtm = pLoop->u.btree.nBtm;
|
||||
int nTop = pLoop->u.btree.nTop;
|
||||
|
||||
/* Variable iLower will be set to the estimate of the number of rows in
|
||||
** the index that are less than the lower bound of the range query. The
|
||||
@@ -1414,8 +1427,6 @@ static int whereRangeScanEst(
|
||||
testcase( pRec->nField!=pBuilder->nRecValid );
|
||||
pRec->nField = pBuilder->nRecValid;
|
||||
}
|
||||
aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq);
|
||||
assert( nEq!=p->nKeyCol || aff==SQLITE_AFF_INTEGER );
|
||||
/* Determine iLower and iUpper using ($P) only. */
|
||||
if( nEq==0 ){
|
||||
iLower = 0;
|
||||
@@ -1434,17 +1445,20 @@ static int whereRangeScanEst(
|
||||
if( p->aSortOrder[nEq] ){
|
||||
/* The roles of pLower and pUpper are swapped for a DESC index */
|
||||
SWAP(WhereTerm*, pLower, pUpper);
|
||||
SWAP(int, nBtm, nTop);
|
||||
}
|
||||
|
||||
/* If possible, improve on the iLower estimate using ($P:$L). */
|
||||
if( pLower ){
|
||||
int bOk; /* True if value is extracted from pExpr */
|
||||
int n; /* Values extracted from pExpr */
|
||||
Expr *pExpr = pLower->pExpr->pRight;
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n);
|
||||
if( rc==SQLITE_OK && n ){
|
||||
tRowcnt iNew;
|
||||
u16 mask = WO_GT|WO_LE;
|
||||
if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
|
||||
iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a);
|
||||
iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
|
||||
iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0);
|
||||
if( iNew>iLower ) iLower = iNew;
|
||||
nOut--;
|
||||
pLower = 0;
|
||||
@@ -1453,13 +1467,15 @@ static int whereRangeScanEst(
|
||||
|
||||
/* If possible, improve on the iUpper estimate using ($P:$U). */
|
||||
if( pUpper ){
|
||||
int bOk; /* True if value is extracted from pExpr */
|
||||
int n; /* Values extracted from pExpr */
|
||||
Expr *pExpr = pUpper->pExpr->pRight;
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n);
|
||||
if( rc==SQLITE_OK && n ){
|
||||
tRowcnt iNew;
|
||||
u16 mask = WO_GT|WO_LE;
|
||||
if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
|
||||
iUprIdx = whereKeyStats(pParse, p, pRec, 1, a);
|
||||
iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
|
||||
iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0);
|
||||
if( iNew<iUpper ) iUpper = iNew;
|
||||
nOut--;
|
||||
pUpper = 0;
|
||||
@@ -1549,7 +1565,6 @@ static int whereEqualScanEst(
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
int nEq = pBuilder->pNew->u.btree.nEq;
|
||||
UnpackedRecord *pRec = pBuilder->pRec;
|
||||
u8 aff; /* Column affinity */
|
||||
int rc; /* Subfunction return code */
|
||||
tRowcnt a[2]; /* Statistics */
|
||||
int bOk;
|
||||
@@ -1573,8 +1588,7 @@ static int whereEqualScanEst(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq-1);
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk);
|
||||
pBuilder->pRec = pRec;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( bOk==0 ) return SQLITE_NOTFOUND;
|
||||
@@ -1663,9 +1677,14 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
|
||||
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
|
||||
}
|
||||
sqlite3DebugPrintf(
|
||||
"TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n",
|
||||
"TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x",
|
||||
iTerm, pTerm, zType, zLeft, pTerm->truthProb,
|
||||
pTerm->eOperator, pTerm->wtFlags);
|
||||
if( pTerm->iField ){
|
||||
sqlite3DebugPrintf(" iField=%d\n", pTerm->iField);
|
||||
}else{
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
|
||||
}
|
||||
}
|
||||
@@ -2187,6 +2206,72 @@ static void whereLoopOutputAdjust(
|
||||
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
|
||||
}
|
||||
|
||||
/*
|
||||
** Term pTerm is a vector range comparison operation. The first comparison
|
||||
** in the vector can be optimized using column nEq of the index. This
|
||||
** function returns the total number of vector elements that can be used
|
||||
** as part of the range comparison.
|
||||
**
|
||||
** For example, if the query is:
|
||||
**
|
||||
** WHERE a = ? AND (b, c, d) > (?, ?, ?)
|
||||
**
|
||||
** and the index:
|
||||
**
|
||||
** CREATE INDEX ... ON (a, b, c, d, e)
|
||||
**
|
||||
** then this function would be invoked with nEq=1. The value returned in
|
||||
** this case is 3.
|
||||
*/
|
||||
int whereRangeVectorLen(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int iCur, /* Cursor open on pIdx */
|
||||
Index *pIdx, /* The index to be used for a inequality constraint */
|
||||
int nEq, /* Number of prior equality constraints on same index */
|
||||
WhereTerm *pTerm /* The vector inequality constraint */
|
||||
){
|
||||
int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
|
||||
int i;
|
||||
|
||||
nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
|
||||
for(i=1; i<nCmp; i++){
|
||||
/* Test if comparison i of pTerm is compatible with column (i+nEq)
|
||||
** of the index. If not, exit the loop. */
|
||||
char aff; /* Comparison affinity */
|
||||
char idxaff = 0; /* Indexed columns affinity */
|
||||
CollSeq *pColl; /* Comparison collation sequence */
|
||||
Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
|
||||
Expr *pRhs = pTerm->pExpr->pRight;
|
||||
if( pRhs->flags & EP_xIsSelect ){
|
||||
pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
|
||||
}else{
|
||||
pRhs = pRhs->x.pList->a[i].pExpr;
|
||||
}
|
||||
|
||||
/* Check that the LHS of the comparison is a column reference to
|
||||
** the right column of the right source table. And that the sort
|
||||
** order of the index column is the same as the sort order of the
|
||||
** leftmost index column. */
|
||||
if( pLhs->op!=TK_COLUMN
|
||||
|| pLhs->iTable!=iCur
|
||||
|| pLhs->iColumn!=pIdx->aiColumn[i+nEq]
|
||||
|| pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq]
|
||||
){
|
||||
break;
|
||||
}
|
||||
|
||||
testcase( pLhs->iColumn==XN_ROWID );
|
||||
aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
|
||||
idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
|
||||
if( aff!=idxaff ) break;
|
||||
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
|
||||
if( pColl==0 ) break;
|
||||
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Adjust the cost C by the costMult facter T. This only occurs if
|
||||
** compiled with -DSQLITE_ENABLE_COSTMULT
|
||||
@@ -2225,6 +2310,8 @@ static int whereLoopAddBtreeIndex(
|
||||
Bitmask saved_prereq; /* Original value of pNew->prereq */
|
||||
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
|
||||
u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
|
||||
u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */
|
||||
u16 saved_nTop; /* Original value of pNew->u.btree.nTop */
|
||||
u16 saved_nSkip; /* Original value of pNew->nSkip */
|
||||
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
|
||||
LogEst saved_nOut; /* Original value of pNew->nOut */
|
||||
@@ -2241,6 +2328,7 @@ static int whereLoopAddBtreeIndex(
|
||||
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
|
||||
opMask = WO_LT|WO_LE;
|
||||
}else{
|
||||
assert( pNew->u.btree.nBtm==0 );
|
||||
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
|
||||
}
|
||||
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
|
||||
@@ -2248,6 +2336,8 @@ static int whereLoopAddBtreeIndex(
|
||||
assert( pNew->u.btree.nEq<pProbe->nColumn );
|
||||
|
||||
saved_nEq = pNew->u.btree.nEq;
|
||||
saved_nBtm = pNew->u.btree.nBtm;
|
||||
saved_nTop = pNew->u.btree.nTop;
|
||||
saved_nSkip = pNew->nSkip;
|
||||
saved_nLTerm = pNew->nLTerm;
|
||||
saved_wsFlags = pNew->wsFlags;
|
||||
@@ -2291,6 +2381,8 @@ static int whereLoopAddBtreeIndex(
|
||||
|
||||
pNew->wsFlags = saved_wsFlags;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
pNew->u.btree.nBtm = saved_nBtm;
|
||||
pNew->u.btree.nTop = saved_nTop;
|
||||
pNew->nLTerm = saved_nLTerm;
|
||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||
pNew->aLTerm[pNew->nLTerm++] = pTerm;
|
||||
@@ -2307,14 +2399,23 @@ static int whereLoopAddBtreeIndex(
|
||||
pNew->wsFlags |= WHERE_COLUMN_IN;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
|
||||
int i;
|
||||
nIn = 46; assert( 46==sqlite3LogEst(25) );
|
||||
|
||||
/* The expression may actually be of the form (x, y) IN (SELECT...).
|
||||
** In this case there is a separate term for each of (x) and (y).
|
||||
** However, the nIn multiplier should only be applied once, not once
|
||||
** for each such term. The following loop checks that pTerm is the
|
||||
** first such term in use, and sets nIn back to 0 if it is not. */
|
||||
for(i=0; i<pNew->nLTerm-1; i++){
|
||||
if( pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0;
|
||||
}
|
||||
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
|
||||
/* "x IN (value, value, ...)" */
|
||||
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
|
||||
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
|
||||
** changes "x IN (?)" into "x=?". */
|
||||
}
|
||||
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
|
||||
** changes "x IN (?)" into "x=?". */
|
||||
|
||||
}else if( eOp & (WO_EQ|WO_IS) ){
|
||||
int iCol = pProbe->aiColumn[saved_nEq];
|
||||
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
||||
@@ -2334,6 +2435,9 @@ static int whereLoopAddBtreeIndex(
|
||||
testcase( eOp & WO_GT );
|
||||
testcase( eOp & WO_GE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
|
||||
pNew->u.btree.nBtm = whereRangeVectorLen(
|
||||
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
|
||||
);
|
||||
pBtm = pTerm;
|
||||
pTop = 0;
|
||||
if( pTerm->wtFlags & TERM_LIKEOPT ){
|
||||
@@ -2346,12 +2450,16 @@ static int whereLoopAddBtreeIndex(
|
||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||
pNew->aLTerm[pNew->nLTerm++] = pTop;
|
||||
pNew->wsFlags |= WHERE_TOP_LIMIT;
|
||||
pNew->u.btree.nTop = 1;
|
||||
}
|
||||
}else{
|
||||
assert( eOp & (WO_LT|WO_LE) );
|
||||
testcase( eOp & WO_LT );
|
||||
testcase( eOp & WO_LE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
|
||||
pNew->u.btree.nTop = whereRangeVectorLen(
|
||||
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
|
||||
);
|
||||
pTop = pTerm;
|
||||
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
|
||||
pNew->aLTerm[pNew->nLTerm-2] : 0;
|
||||
@@ -2451,6 +2559,8 @@ static int whereLoopAddBtreeIndex(
|
||||
}
|
||||
pNew->prereq = saved_prereq;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
pNew->u.btree.nBtm = saved_nBtm;
|
||||
pNew->u.btree.nTop = saved_nTop;
|
||||
pNew->nSkip = saved_nSkip;
|
||||
pNew->wsFlags = saved_wsFlags;
|
||||
pNew->nOut = saved_nOut;
|
||||
@@ -2572,7 +2682,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
|
||||
|
||||
/*
|
||||
** Add all WhereLoop objects for a single table of the join where the table
|
||||
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
|
||||
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
|
||||
** a b-tree table, not a virtual table.
|
||||
**
|
||||
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
|
||||
@@ -2726,6 +2836,8 @@ static int whereLoopAddBtree(
|
||||
}
|
||||
rSize = pProbe->aiRowLogEst[0];
|
||||
pNew->u.btree.nEq = 0;
|
||||
pNew->u.btree.nBtm = 0;
|
||||
pNew->u.btree.nTop = 0;
|
||||
pNew->nSkip = 0;
|
||||
pNew->nLTerm = 0;
|
||||
pNew->iSortIdx = 0;
|
||||
@@ -2854,6 +2966,7 @@ static int whereLoopAddVirtualOne(
|
||||
Bitmask mUsable, /* Mask of usable tables */
|
||||
u16 mExclude, /* Exclude terms using these operators */
|
||||
sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
|
||||
u16 mNoOmit, /* Do not omit these constraints */
|
||||
int *pbIn /* OUT: True if plan uses an IN(...) op */
|
||||
){
|
||||
WhereClause *pWC = pBuilder->pWC;
|
||||
@@ -2942,6 +3055,7 @@ static int whereLoopAddVirtualOne(
|
||||
}
|
||||
}
|
||||
}
|
||||
pNew->u.vtab.omitMask &= ~mNoOmit;
|
||||
|
||||
pNew->nLTerm = mxTerm+1;
|
||||
assert( pNew->nLTerm<=pNew->nLSlot );
|
||||
@@ -3015,6 +3129,7 @@ static int whereLoopAddVirtual(
|
||||
int bIn; /* True if plan uses IN(...) operator */
|
||||
WhereLoop *pNew;
|
||||
Bitmask mBest; /* Tables used by best possible plan */
|
||||
u16 mNoOmit;
|
||||
|
||||
assert( (mPrereq & mUnusable)==0 );
|
||||
pWInfo = pBuilder->pWInfo;
|
||||
@@ -3023,7 +3138,8 @@ static int whereLoopAddVirtual(
|
||||
pNew = pBuilder->pNew;
|
||||
pSrc = &pWInfo->pTabList->a[pNew->iTab];
|
||||
assert( IsVirtual(pSrc->pTab) );
|
||||
p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy);
|
||||
p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy,
|
||||
&mNoOmit);
|
||||
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pNew->rSetup = 0;
|
||||
pNew->wsFlags = WHERE_VIRTUALTABLE;
|
||||
@@ -3037,7 +3153,7 @@ static int whereLoopAddVirtual(
|
||||
|
||||
/* First call xBestIndex() with all constraints usable. */
|
||||
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn);
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
|
||||
|
||||
/* If the call to xBestIndex() with all terms enabled produced a plan
|
||||
** that does not require any source tables (IOW: a plan with mBest==0),
|
||||
@@ -3054,7 +3170,8 @@ static int whereLoopAddVirtual(
|
||||
** xBestIndex again, this time with IN(...) terms disabled. */
|
||||
if( bIn ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn);
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
|
||||
assert( bIn==0 );
|
||||
mBestNoIn = pNew->prereq & ~mPrereq;
|
||||
if( mBestNoIn==0 ){
|
||||
@@ -3080,7 +3197,8 @@ static int whereLoopAddVirtual(
|
||||
if( mNext==mBest || mNext==mBestNoIn ) continue;
|
||||
WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
|
||||
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mNext|mPrereq, 0, p, &bIn);
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
|
||||
if( pNew->prereq==mPrereq ){
|
||||
seenZero = 1;
|
||||
if( bIn==0 ) seenZeroNoIN = 1;
|
||||
@@ -3092,7 +3210,8 @@ static int whereLoopAddVirtual(
|
||||
** usable), make a call here with all source tables disabled */
|
||||
if( rc==SQLITE_OK && seenZero==0 ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn);
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
|
||||
if( bIn==0 ) seenZeroNoIN = 1;
|
||||
}
|
||||
|
||||
@@ -3101,7 +3220,8 @@ static int whereLoopAddVirtual(
|
||||
** operator, make a final call to obtain one here. */
|
||||
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn);
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3401,6 +3521,14 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
|
||||
~ready, eqOpMask, 0);
|
||||
if( pTerm==0 ) continue;
|
||||
if( pTerm->eOperator==WO_IN ){
|
||||
/* IN terms are only valid for sorting in the ORDER BY LIMIT
|
||||
** optimization, and then only if they are actually used
|
||||
** by the query plan */
|
||||
assert( wctrlFlags & WHERE_ORDERBY_LIMIT );
|
||||
for(j=0; j<pLoop->nLTerm && pTerm!=pLoop->aLTerm[j]; j++){}
|
||||
if( j>=pLoop->nLTerm ) continue;
|
||||
}
|
||||
if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
|
||||
const char *z1, *z2;
|
||||
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
|
||||
@@ -3437,20 +3565,42 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
rev = revSet = 0;
|
||||
distinctColumns = 0;
|
||||
for(j=0; j<nColumn; j++){
|
||||
u8 bOnce; /* True to run the ORDER BY search loop */
|
||||
u8 bOnce = 1; /* True to run the ORDER BY search loop */
|
||||
|
||||
/* Skip over == and IS and ISNULL terms.
|
||||
** (Also skip IN terms when doing WHERE_ORDERBY_LIMIT processing)
|
||||
*/
|
||||
if( j<pLoop->u.btree.nEq
|
||||
&& pLoop->nSkip==0
|
||||
&& ((i = pLoop->aLTerm[j]->eOperator) & eqOpMask)!=0
|
||||
){
|
||||
if( i & WO_ISNULL ){
|
||||
testcase( isOrderDistinct );
|
||||
isOrderDistinct = 0;
|
||||
assert( j>=pLoop->u.btree.nEq
|
||||
|| (pLoop->aLTerm[j]==0)==(j<pLoop->nSkip)
|
||||
);
|
||||
if( j<pLoop->u.btree.nEq && j>=pLoop->nSkip ){
|
||||
u16 eOp = pLoop->aLTerm[j]->eOperator;
|
||||
|
||||
/* Skip over == and IS and ISNULL terms. (Also skip IN terms when
|
||||
** doing WHERE_ORDERBY_LIMIT processing).
|
||||
**
|
||||
** If the current term is a column of an ((?,?) IN (SELECT...))
|
||||
** expression for which the SELECT returns more than one column,
|
||||
** check that it is the only column used by this loop. Otherwise,
|
||||
** if it is one of two or more, none of the columns can be
|
||||
** considered to match an ORDER BY term. */
|
||||
if( (eOp & eqOpMask)!=0 ){
|
||||
if( eOp & WO_ISNULL ){
|
||||
testcase( isOrderDistinct );
|
||||
isOrderDistinct = 0;
|
||||
}
|
||||
continue;
|
||||
}else if( ALWAYS(eOp & WO_IN) ){
|
||||
/* ALWAYS() justification: eOp is an equality operator due to the
|
||||
** j<pLoop->u.btree.nEq constraint above. Any equality other
|
||||
** than WO_IN is captured by the previous "if". So this one
|
||||
** always has to be WO_IN. */
|
||||
Expr *pX = pLoop->aLTerm[j]->pExpr;
|
||||
for(i=j+1; i<pLoop->u.btree.nEq; i++){
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ){
|
||||
assert( (pLoop->aLTerm[i]->eOperator & WO_IN) );
|
||||
bOnce = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the column number in the table (iColumn) and sort order
|
||||
@@ -3479,7 +3629,6 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
/* Find the ORDER BY term that corresponds to the j-th column
|
||||
** of the index and mark that ORDER BY term off
|
||||
*/
|
||||
bOnce = 1;
|
||||
isMatch = 0;
|
||||
for(i=0; bOnce && i<nOrderBy; i++){
|
||||
if( MASKBIT(i) & obSat ) continue;
|
||||
@@ -4673,10 +4822,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
|
||||
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
|
||||
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
|
||||
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
|
||||
VdbeCoverage(v);
|
||||
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
|
||||
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
|
||||
if( pIn->eEndLoopOp!=OP_Noop ){
|
||||
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
|
||||
VdbeCoverage(v);
|
||||
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
|
||||
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ struct WhereLoop {
|
||||
union {
|
||||
struct { /* Information for internal btree tables */
|
||||
u16 nEq; /* Number of equality constraints */
|
||||
u16 nBtm; /* Size of BTM vector */
|
||||
u16 nTop; /* Size of TOP vector */
|
||||
Index *pIndex; /* Index used, or NULL */
|
||||
} btree;
|
||||
struct { /* Information for virtual tables */
|
||||
@@ -246,6 +248,7 @@ struct WhereTerm {
|
||||
Expr *pExpr; /* Pointer to the subexpression that is this term */
|
||||
int iParent; /* Disable pWC->a[iParent] when this term disabled */
|
||||
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
|
||||
int iField; /* Field in (?,?,?) IN (SELECT...) vector */
|
||||
union {
|
||||
int leftColumn; /* Column number of X in "X <op> <expr>" */
|
||||
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
|
||||
|
||||
+267
-71
@@ -21,6 +21,17 @@
|
||||
#include "whereInt.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
|
||||
/*
|
||||
** Return the name of the i-th column of the pIdx index.
|
||||
*/
|
||||
static const char *explainIndexColumnName(Index *pIdx, int i){
|
||||
i = pIdx->aiColumn[i];
|
||||
if( i==XN_EXPR ) return "<expr>";
|
||||
if( i==XN_ROWID ) return "rowid";
|
||||
return pIdx->pTable->aCol[i].zName;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is a helper for explainIndexRange() below
|
||||
**
|
||||
@@ -31,24 +42,32 @@
|
||||
*/
|
||||
static void explainAppendTerm(
|
||||
StrAccum *pStr, /* The text expression being built */
|
||||
int iTerm, /* Index of this term. First is zero */
|
||||
const char *zColumn, /* Name of the column */
|
||||
Index *pIdx, /* Index to read column names from */
|
||||
int nTerm, /* Number of terms */
|
||||
int iTerm, /* Zero-based index of first term. */
|
||||
int bAnd, /* Non-zero to append " AND " */
|
||||
const char *zOp /* Name of the operator */
|
||||
){
|
||||
if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
sqlite3StrAccumAppendAll(pStr, zColumn);
|
||||
sqlite3StrAccumAppend(pStr, zOp, 1);
|
||||
sqlite3StrAccumAppend(pStr, "?", 1);
|
||||
}
|
||||
int i;
|
||||
|
||||
/*
|
||||
** Return the name of the i-th column of the pIdx index.
|
||||
*/
|
||||
static const char *explainIndexColumnName(Index *pIdx, int i){
|
||||
i = pIdx->aiColumn[i];
|
||||
if( i==XN_EXPR ) return "<expr>";
|
||||
if( i==XN_ROWID ) return "rowid";
|
||||
return pIdx->pTable->aCol[i].zName;
|
||||
assert( nTerm>=1 );
|
||||
if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
|
||||
sqlite3StrAccumAppend(pStr, zOp, 1);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppend(pStr, "?", 1);
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -81,12 +100,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
|
||||
j = i;
|
||||
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
|
||||
const char *z = explainIndexColumnName(pIndex, i);
|
||||
explainAppendTerm(pStr, i++, z, ">");
|
||||
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nBtm, j, i, ">");
|
||||
i = 1;
|
||||
}
|
||||
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
|
||||
const char *z = explainIndexColumnName(pIndex, j);
|
||||
explainAppendTerm(pStr, i, z, "<");
|
||||
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
}
|
||||
@@ -276,7 +294,7 @@ void sqlite3WhereAddScanStatus(
|
||||
*/
|
||||
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
||||
int nLoop = 0;
|
||||
while( pTerm
|
||||
while( ALWAYS(pTerm!=0)
|
||||
&& (pTerm->wtFlags & TERM_CODED)==0
|
||||
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
|
||||
&& (pLevel->notReady & pTerm->prereqAll)==0
|
||||
@@ -332,16 +350,46 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Expression pRight, which is the RHS of a comparison operation, is
|
||||
** either a vector of n elements or, if n==1, a scalar expression.
|
||||
** Before the comparison operation, affinity zAff is to be applied
|
||||
** to the pRight values. This function modifies characters within the
|
||||
** affinity string to SQLITE_AFF_BLOB if either:
|
||||
**
|
||||
** * the comparison will be performed with no affinity, or
|
||||
** * the affinity change in zAff is guaranteed not to change the value.
|
||||
*/
|
||||
static void updateRangeAffinityStr(
|
||||
Parse *pParse, /* Parse context */
|
||||
Expr *pRight, /* RHS of comparison */
|
||||
int n, /* Number of vector elements in comparison */
|
||||
char *zAff /* Affinity string to modify */
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<n; i++){
|
||||
Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
|
||||
if( sqlite3CompareAffinity(p, zAff[i])==SQLITE_AFF_BLOB
|
||||
|| sqlite3ExprNeedsNoAffinityChange(p, zAff[i])
|
||||
){
|
||||
zAff[i] = SQLITE_AFF_BLOB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for a single equality term of the WHERE clause. An equality
|
||||
** term can be either X=expr or X IN (...). pTerm is the term to be
|
||||
** coded.
|
||||
**
|
||||
** The current value for the constraint is left in register iReg.
|
||||
** The current value for the constraint is left in a register, the index
|
||||
** of which is returned. An attempt is made store the result in iTarget but
|
||||
** this is only guaranteed for TK_ISNULL and TK_IN constraints. If the
|
||||
** constraint is a TK_EQ or TK_IS, then the current value might be left in
|
||||
** some other register and it is the caller's responsibility to compensate.
|
||||
**
|
||||
** For a constraint of the form X=expr, the expression is evaluated and its
|
||||
** result is left on the stack. For constraints of the form X IN (...)
|
||||
** For a constraint of the form X=expr, the expression is evaluated in
|
||||
** straight-line code. For constraints of the form X IN (...)
|
||||
** this routine sets up a loop that will iterate over all values of X.
|
||||
*/
|
||||
static int codeEqualityTerm(
|
||||
@@ -356,6 +404,7 @@ static int codeEqualityTerm(
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int iReg; /* Register holding results */
|
||||
|
||||
assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
|
||||
assert( iTarget>0 );
|
||||
if( pX->op==TK_EQ || pX->op==TK_IS ){
|
||||
iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
|
||||
@@ -364,10 +413,13 @@ static int codeEqualityTerm(
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
}else{
|
||||
int eType;
|
||||
int eType = IN_INDEX_NOOP;
|
||||
int iTab;
|
||||
struct InLoop *pIn;
|
||||
WhereLoop *pLoop = pLevel->pWLoop;
|
||||
int i;
|
||||
int nEq = 0;
|
||||
int *aiMap = 0;
|
||||
|
||||
if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
|
||||
&& pLoop->u.btree.pIndex!=0
|
||||
@@ -379,7 +431,75 @@ static int codeEqualityTerm(
|
||||
}
|
||||
assert( pX->op==TK_IN );
|
||||
iReg = iTarget;
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
|
||||
|
||||
for(i=0; i<iEq; i++){
|
||||
if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
|
||||
disableTerm(pLevel, pTerm);
|
||||
return iTarget;
|
||||
}
|
||||
}
|
||||
for(i=iEq;i<pLoop->nLTerm; i++){
|
||||
if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
|
||||
}
|
||||
|
||||
if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
|
||||
}else{
|
||||
Select *pSelect = pX->x.pSelect;
|
||||
sqlite3 *db = pParse->db;
|
||||
ExprList *pOrigRhs = pSelect->pEList;
|
||||
ExprList *pOrigLhs = pX->pLeft->x.pList;
|
||||
ExprList *pRhs = 0; /* New Select.pEList for RHS */
|
||||
ExprList *pLhs = 0; /* New pX->pLeft vector */
|
||||
|
||||
for(i=iEq;i<pLoop->nLTerm; i++){
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ){
|
||||
int iField = pLoop->aLTerm[i]->iField - 1;
|
||||
Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
|
||||
Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
|
||||
|
||||
pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
|
||||
pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
|
||||
}
|
||||
}
|
||||
if( !db->mallocFailed ){
|
||||
Expr *pLeft = pX->pLeft;
|
||||
|
||||
if( pSelect->pOrderBy ){
|
||||
/* If the SELECT statement has an ORDER BY clause, zero the
|
||||
** iOrderByCol variables. These are set to non-zero when an
|
||||
** ORDER BY term exactly matches one of the terms of the
|
||||
** result-set. Since the result-set of the SELECT statement may
|
||||
** have been modified or reordered, these variables are no longer
|
||||
** set correctly. Since setting them is just an optimization,
|
||||
** it's easiest just to zero them here. */
|
||||
ExprList *pOrderBy = pSelect->pOrderBy;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
pOrderBy->a[i].u.x.iOrderByCol = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take care here not to generate a TK_VECTOR containing only a
|
||||
** single value. Since the parser never creates such a vector, some
|
||||
** of the subroutines do not handle this case. */
|
||||
if( pLhs->nExpr==1 ){
|
||||
pX->pLeft = pLhs->a[0].pExpr;
|
||||
}else{
|
||||
pLeft->x.pList = pLhs;
|
||||
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
|
||||
testcase( aiMap==0 );
|
||||
}
|
||||
pSelect->pEList = pRhs;
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
|
||||
testcase( aiMap!=0 && aiMap[0]!=0 );
|
||||
pSelect->pEList = pOrigRhs;
|
||||
pLeft->x.pList = pOrigLhs;
|
||||
pX->pLeft = pLeft;
|
||||
}
|
||||
sqlite3ExprListDelete(pParse->db, pLhs);
|
||||
sqlite3ExprListDelete(pParse->db, pRhs);
|
||||
}
|
||||
|
||||
if( eType==IN_INDEX_INDEX_DESC ){
|
||||
testcase( bRev );
|
||||
bRev = !bRev;
|
||||
@@ -389,28 +509,46 @@ static int codeEqualityTerm(
|
||||
VdbeCoverageIf(v, bRev);
|
||||
VdbeCoverageIf(v, !bRev);
|
||||
assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
|
||||
|
||||
pLoop->wsFlags |= WHERE_IN_ABLE;
|
||||
if( pLevel->u.in.nIn==0 ){
|
||||
pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
|
||||
}
|
||||
pLevel->u.in.nIn++;
|
||||
|
||||
i = pLevel->u.in.nIn;
|
||||
pLevel->u.in.nIn += nEq;
|
||||
pLevel->u.in.aInLoop =
|
||||
sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
|
||||
sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
|
||||
pIn = pLevel->u.in.aInLoop;
|
||||
if( pIn ){
|
||||
pIn += pLevel->u.in.nIn - 1;
|
||||
pIn->iCur = iTab;
|
||||
if( eType==IN_INDEX_ROWID ){
|
||||
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
|
||||
}else{
|
||||
pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
|
||||
int iMap = 0; /* Index in aiMap[] */
|
||||
pIn += i;
|
||||
for(i=iEq;i<pLoop->nLTerm; i++){
|
||||
int iOut = iReg;
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ){
|
||||
if( eType==IN_INDEX_ROWID ){
|
||||
assert( nEq==1 && i==iEq );
|
||||
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
|
||||
}else{
|
||||
int iCol = aiMap ? aiMap[iMap++] : 0;
|
||||
iOut = iReg + i - iEq;
|
||||
pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
|
||||
if( i==iEq ){
|
||||
pIn->iCur = iTab;
|
||||
pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
|
||||
}else{
|
||||
pIn->eEndLoopOp = OP_Noop;
|
||||
}
|
||||
pIn++;
|
||||
}
|
||||
}
|
||||
pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
|
||||
sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
|
||||
}else{
|
||||
pLevel->u.in.nIn = 0;
|
||||
}
|
||||
sqlite3DbFree(pParse->db, aiMap);
|
||||
#endif
|
||||
}
|
||||
disableTerm(pLevel, pTerm);
|
||||
@@ -536,9 +674,15 @@ static int codeAllEqualityTerms(
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
|
||||
}
|
||||
}
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
if( pTerm->pExpr->flags & EP_xIsSelect ){
|
||||
/* No affinity ever needs to be (or should be) applied to a value
|
||||
** from the RHS of an "? IN (SELECT ...)" expression. The
|
||||
** sqlite3FindInIndex() routine has already ensured that the
|
||||
** affinity of the comparison has been applied to the value. */
|
||||
if( zAff ) zAff[j] = SQLITE_AFF_BLOB;
|
||||
}
|
||||
}else if( (pTerm->eOperator & WO_ISNULL)==0 ){
|
||||
Expr *pRight = pTerm->pExpr->pRight;
|
||||
if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
|
||||
@@ -861,6 +1005,39 @@ static void codeDeferredSeek(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the expression passed as the second argument is a vector, generate
|
||||
** code to write the first nReg elements of the vector into an array
|
||||
** of registers starting with iReg.
|
||||
**
|
||||
** If the expression is not a vector, then nReg must be passed 1. In
|
||||
** this case, generate code to evaluate the expression and leave the
|
||||
** result in register iReg.
|
||||
*/
|
||||
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
||||
assert( nReg>0 );
|
||||
if( sqlite3ExprIsVector(p) ){
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( (p->flags & EP_xIsSelect) ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
int i;
|
||||
ExprList *pList = p->x.pList;
|
||||
assert( nReg<=pList->nExpr );
|
||||
for(i=0; i<nReg; i++){
|
||||
sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
assert( nReg==1 );
|
||||
sqlite3ExprCode(pParse, p, iReg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
||||
** implementation described by pWInfo.
|
||||
@@ -956,7 +1133,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
|
||||
addrNotFound = pLevel->addrNxt;
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
|
||||
Expr *pRight = pTerm->pExpr->pRight;
|
||||
codeExprOrVector(pParse, pRight, iTarget, 1);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
|
||||
@@ -1070,6 +1248,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( pStart ){
|
||||
Expr *pX; /* The expression that defines the start bound */
|
||||
int r1, rTemp; /* Registers for holding the start boundary */
|
||||
int op; /* Cursor seek operation */
|
||||
|
||||
/* The following constant maps TK_xx codes into corresponding
|
||||
** seek opcodes. It depends on a particular ordering of TK_xx
|
||||
@@ -1089,8 +1268,16 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pX = pStart->pExpr;
|
||||
assert( pX!=0 );
|
||||
testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
|
||||
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
|
||||
if( sqlite3ExprIsVector(pX->pRight) ){
|
||||
r1 = rTemp = sqlite3GetTempReg(pParse);
|
||||
codeExprOrVector(pParse, pX->pRight, r1, 1);
|
||||
op = aMoveOp[(pX->op - TK_GT) | 0x0001];
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
|
||||
disableTerm(pLevel, pStart);
|
||||
op = aMoveOp[(pX->op - TK_GT)];
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, op, iCur, addrBrk, r1);
|
||||
VdbeComment((v, "pk"));
|
||||
VdbeCoverageIf(v, pX->op==TK_GT);
|
||||
VdbeCoverageIf(v, pX->op==TK_LE);
|
||||
@@ -1098,7 +1285,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
VdbeCoverageIf(v, pX->op==TK_GE);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
|
||||
sqlite3ReleaseTempReg(pParse, rTemp);
|
||||
disableTerm(pLevel, pStart);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
|
||||
VdbeCoverageIf(v, bRev==0);
|
||||
@@ -1112,13 +1298,17 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
|
||||
testcase( pEnd->wtFlags & TERM_VIRTUAL );
|
||||
memEndValue = ++pParse->nMem;
|
||||
sqlite3ExprCode(pParse, pX->pRight, memEndValue);
|
||||
if( pX->op==TK_LT || pX->op==TK_GT ){
|
||||
codeExprOrVector(pParse, pX->pRight, memEndValue, 1);
|
||||
if( 0==sqlite3ExprIsVector(pX->pRight)
|
||||
&& (pX->op==TK_LT || pX->op==TK_GT)
|
||||
){
|
||||
testOp = bRev ? OP_Le : OP_Ge;
|
||||
}else{
|
||||
testOp = bRev ? OP_Lt : OP_Gt;
|
||||
}
|
||||
disableTerm(pLevel, pEnd);
|
||||
if( 0==sqlite3ExprIsVector(pX->pRight) ){
|
||||
disableTerm(pLevel, pEnd);
|
||||
}
|
||||
}
|
||||
start = sqlite3VdbeCurrentAddr(v);
|
||||
pLevel->op = bRev ? OP_Prev : OP_Next;
|
||||
@@ -1185,6 +1375,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
|
||||
};
|
||||
u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
|
||||
u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */
|
||||
u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */
|
||||
int regBase; /* Base register holding constraint values */
|
||||
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
|
||||
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
|
||||
@@ -1197,7 +1389,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
int nExtraReg = 0; /* Number of extra registers needed */
|
||||
int op; /* Instruction opcode */
|
||||
char *zStartAff; /* Affinity for start of range constraint */
|
||||
char cEndAff = 0; /* Affinity for end of range constraint */
|
||||
char *zEndAff = 0; /* Affinity for end of range constraint */
|
||||
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
|
||||
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
|
||||
|
||||
@@ -1231,14 +1423,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
j = nEq;
|
||||
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
|
||||
pRangeStart = pLoop->aLTerm[j++];
|
||||
nExtraReg = 1;
|
||||
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
|
||||
/* Like optimization range constraints always occur in pairs */
|
||||
assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
|
||||
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
|
||||
}
|
||||
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
|
||||
pRangeEnd = pLoop->aLTerm[j++];
|
||||
nExtraReg = 1;
|
||||
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop);
|
||||
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
||||
if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
|
||||
assert( pRangeStart!=0 ); /* LIKE opt constraints */
|
||||
@@ -1274,6 +1466,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
){
|
||||
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
||||
SWAP(u8, bSeekPastNull, bStopAtNull);
|
||||
SWAP(u8, nBtm, nTop);
|
||||
}
|
||||
|
||||
/* Generate code to evaluate all constraint terms using == or IN
|
||||
@@ -1283,7 +1476,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
|
||||
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
|
||||
if( zStartAff ) cEndAff = zStartAff[nEq];
|
||||
if( zStartAff && nTop ){
|
||||
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
|
||||
}
|
||||
addrNxt = pLevel->addrNxt;
|
||||
|
||||
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
|
||||
@@ -1298,7 +1493,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
nConstraint = nEq;
|
||||
if( pRangeStart ){
|
||||
Expr *pRight = pRangeStart->pExpr->pRight;
|
||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
||||
codeExprOrVector(pParse, pRight, regBase+nEq, nBtm);
|
||||
whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
|
||||
if( (pRangeStart->wtFlags & TERM_VNULL)==0
|
||||
&& sqlite3ExprCanBeNull(pRight)
|
||||
@@ -1307,18 +1502,15 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
if( zStartAff ){
|
||||
if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_BLOB){
|
||||
/* Since the comparison is to be performed with no conversions
|
||||
** applied to the operands, set the affinity to apply to pRight to
|
||||
** SQLITE_AFF_BLOB. */
|
||||
zStartAff[nEq] = SQLITE_AFF_BLOB;
|
||||
}
|
||||
if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
|
||||
zStartAff[nEq] = SQLITE_AFF_BLOB;
|
||||
}
|
||||
updateRangeAffinityStr(pParse, pRight, nBtm, &zStartAff[nEq]);
|
||||
}
|
||||
nConstraint++;
|
||||
nConstraint += nBtm;
|
||||
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
|
||||
if( sqlite3ExprIsVector(pRight)==0 ){
|
||||
disableTerm(pLevel, pRangeStart);
|
||||
}else{
|
||||
startEq = 1;
|
||||
}
|
||||
bSeekPastNull = 0;
|
||||
}else if( bSeekPastNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
||||
@@ -1351,7 +1543,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( pRangeEnd ){
|
||||
Expr *pRight = pRangeEnd->pExpr->pRight;
|
||||
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
||||
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
|
||||
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
|
||||
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
||||
&& sqlite3ExprCanBeNull(pRight)
|
||||
@@ -1359,19 +1551,27 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_BLOB
|
||||
&& !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
|
||||
){
|
||||
codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
|
||||
if( zEndAff ){
|
||||
updateRangeAffinityStr(pParse, pRight, nTop, zEndAff);
|
||||
codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff);
|
||||
}else{
|
||||
assert( pParse->db->mallocFailed );
|
||||
}
|
||||
nConstraint++;
|
||||
nConstraint += nTop;
|
||||
testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
|
||||
|
||||
if( sqlite3ExprIsVector(pRight)==0 ){
|
||||
disableTerm(pLevel, pRangeEnd);
|
||||
}else{
|
||||
endEq = 1;
|
||||
}
|
||||
}else if( bStopAtNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
||||
endEq = 0;
|
||||
nConstraint++;
|
||||
}
|
||||
sqlite3DbFree(db, zStartAff);
|
||||
sqlite3DbFree(db, zEndAff);
|
||||
|
||||
/* Top of the loop body */
|
||||
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
|
||||
@@ -1387,8 +1587,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
|
||||
/* Seek the table cursor, if required */
|
||||
disableTerm(pLevel, pRangeStart);
|
||||
disableTerm(pLevel, pRangeEnd);
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
}else if( HasRowid(pIdx->pTable) ){
|
||||
@@ -1412,9 +1610,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* Record the instruction used to terminate the loop. Disable
|
||||
** WHERE clause terms made redundant by the index range scan.
|
||||
*/
|
||||
/* Record the instruction used to terminate the loop. */
|
||||
if( pLoop->wsFlags & WHERE_ONEROW ){
|
||||
pLevel->op = OP_Noop;
|
||||
}else if( bRev ){
|
||||
@@ -1491,7 +1687,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
u16 wctrlFlags; /* Flags for sub-WHERE clause */
|
||||
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
||||
Table *pTab = pTabItem->pTab;
|
||||
|
||||
|
||||
pTerm = pLoop->aLTerm[0];
|
||||
assert( pTerm!=0 );
|
||||
assert( pTerm->eOperator & WO_OR );
|
||||
|
||||
+85
-9
@@ -95,7 +95,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
|
||||
/*
|
||||
** Return TRUE if the given operator is one of the operators that is
|
||||
** allowed for an indexable WHERE clause term. The allowed operators are
|
||||
** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
|
||||
** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL"
|
||||
*/
|
||||
static int allowedOp(int op){
|
||||
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
|
||||
@@ -290,7 +290,7 @@ static int isMatchOfColumn(
|
||||
Expr *pExpr, /* Test this expression */
|
||||
unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
|
||||
){
|
||||
struct Op2 {
|
||||
static const struct Op2 {
|
||||
const char *zOp;
|
||||
unsigned char eOp2;
|
||||
} aOp[] = {
|
||||
@@ -823,7 +823,8 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
|
||||
** in any index. Return TRUE (1) if pExpr is an indexed term and return
|
||||
** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor
|
||||
** number of the table that is indexed and *piColumn to the column number
|
||||
** of the column that is indexed, or -2 if an expression is being indexed.
|
||||
** of the column that is indexed, or XN_EXPR (-2) if an expression is being
|
||||
** indexed.
|
||||
**
|
||||
** If pExpr is a TK_COLUMN column reference, then this routine always returns
|
||||
** true even if that particular column is not indexed, because the column
|
||||
@@ -831,6 +832,7 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
|
||||
*/
|
||||
static int exprMightBeIndexed(
|
||||
SrcList *pFrom, /* The FROM clause */
|
||||
int op, /* The specific comparison operator */
|
||||
Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
|
||||
Expr *pExpr, /* An operand of a comparison operator */
|
||||
int *piCur, /* Write the referenced table cursor number here */
|
||||
@@ -839,6 +841,17 @@ static int exprMightBeIndexed(
|
||||
Index *pIdx;
|
||||
int i;
|
||||
int iCur;
|
||||
|
||||
/* If this expression is a vector to the left or right of a
|
||||
** inequality constraint (>, <, >= or <=), perform the processing
|
||||
** on the first element of the vector. */
|
||||
assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
|
||||
assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
|
||||
assert( op<=TK_GE );
|
||||
if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
|
||||
pExpr = pExpr->x.pList->a[0].pExpr;
|
||||
}
|
||||
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
*piCur = pExpr->iTable;
|
||||
*piColumn = pExpr->iColumn;
|
||||
@@ -851,10 +864,10 @@ static int exprMightBeIndexed(
|
||||
for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->aColExpr==0 ) continue;
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
if( pIdx->aiColumn[i]!=(-2) ) continue;
|
||||
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
|
||||
if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
|
||||
*piCur = iCur;
|
||||
*piColumn = -2;
|
||||
*piColumn = XN_EXPR;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -911,6 +924,7 @@ static void exprAnalyze(
|
||||
op = pExpr->op;
|
||||
if( op==TK_IN ){
|
||||
assert( pExpr->pRight==0 );
|
||||
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
|
||||
}else{
|
||||
@@ -937,18 +951,26 @@ static void exprAnalyze(
|
||||
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
|
||||
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
|
||||
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
|
||||
if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){
|
||||
|
||||
if( pTerm->iField>0 ){
|
||||
assert( op==TK_IN );
|
||||
assert( pLeft->op==TK_VECTOR );
|
||||
pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
|
||||
}
|
||||
|
||||
if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
|
||||
pTerm->leftCursor = iCur;
|
||||
pTerm->u.leftColumn = iColumn;
|
||||
pTerm->eOperator = operatorMask(op) & opMask;
|
||||
}
|
||||
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
|
||||
if( pRight
|
||||
&& exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn)
|
||||
&& exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn)
|
||||
){
|
||||
WhereTerm *pNew;
|
||||
Expr *pDup;
|
||||
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
|
||||
assert( pTerm->iField==0 );
|
||||
if( pTerm->leftCursor>=0 ){
|
||||
int idxNew;
|
||||
pDup = sqlite3ExprDup(db, pExpr, 0);
|
||||
@@ -1152,6 +1174,59 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
|
||||
** new terms for each component comparison - "a = ?" and "b = ?". The
|
||||
** new terms completely replace the original vector comparison, which is
|
||||
** no longer used.
|
||||
**
|
||||
** This is only required if at least one side of the comparison operation
|
||||
** is not a sub-select. */
|
||||
if( pWC->op==TK_AND
|
||||
&& (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||
&& sqlite3ExprIsVector(pExpr->pLeft)
|
||||
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|
||||
|| (pExpr->pRight->flags & EP_xIsSelect)==0
|
||||
)){
|
||||
int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||
int i;
|
||||
assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
|
||||
for(i=0; i<nLeft; i++){
|
||||
int idxNew;
|
||||
Expr *pNew;
|
||||
Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
|
||||
Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
|
||||
|
||||
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->eOperator = 0;
|
||||
}
|
||||
|
||||
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
|
||||
** a virtual term for each vector component. The expression object
|
||||
** used by each such virtual term is pExpr (the full vector IN(...)
|
||||
** expression). The WhereTerm.iField variable identifies the index within
|
||||
** the vector on the LHS that the virtual term represents.
|
||||
**
|
||||
** This only works if the RHS is a simple SELECT, not a compound
|
||||
*/
|
||||
if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
|
||||
&& pExpr->pLeft->op==TK_VECTOR
|
||||
&& pExpr->x.pSelect->pPrior==0
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
|
||||
int idxNew;
|
||||
idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
|
||||
pWC->a[idxNew].iField = i+1;
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
/* When sqlite_stat3 histogram data is available an operator of the
|
||||
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||
@@ -1275,13 +1350,14 @@ void sqlite3WhereClauseClear(WhereClause *pWC){
|
||||
** tree.
|
||||
*/
|
||||
Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
||||
Bitmask mask = 0;
|
||||
Bitmask mask;
|
||||
if( p==0 ) return 0;
|
||||
if( p->op==TK_COLUMN ){
|
||||
mask = sqlite3WhereGetMask(pMaskSet, p->iTable);
|
||||
return mask;
|
||||
}
|
||||
mask = sqlite3WhereExprUsage(pMaskSet, p->pRight);
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly) );
|
||||
mask = p->pRight ? sqlite3WhereExprUsage(pMaskSet, p->pRight) : 0;
|
||||
if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
mask |= exprSelectUsage(pMaskSet, p->x.pSelect);
|
||||
|
||||
@@ -84,4 +84,35 @@ do_malloc_test backup_malloc-2 -tclprep {
|
||||
db2 close
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA page_size = 16384;
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_faultsim_test 3 -faults oom* -prep {
|
||||
catch { db close }
|
||||
|
||||
forcedelete test2.db
|
||||
sqlite3 db2 test2.db
|
||||
sqlite3 db test.db
|
||||
sqlite3_backup B db2 main db main
|
||||
} -body {
|
||||
|
||||
set rc [B step 50]
|
||||
if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} {
|
||||
error "out of memory"
|
||||
}
|
||||
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
faultsim_integrity_check
|
||||
|
||||
# Finalize the backup.
|
||||
catch { B finish }
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -389,5 +389,19 @@ do_test delete-9.5 {
|
||||
set res
|
||||
} {1 a b 1 c d 2 a b 2 c d}
|
||||
|
||||
do_execsql_test delete-10.0 {
|
||||
CREATE TABLE t1(a INT UNIQUE, b INT);
|
||||
INSERT INTO t1(a,b) VALUES('1','2');
|
||||
SELECT * FROM t1 WHERE a='1' AND b='2';
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test delete-10.1 {
|
||||
DELETE FROM t1 WHERE a='1' AND b='2';
|
||||
}
|
||||
|
||||
do_execsql_test delete-10.2 {
|
||||
SELECT * FROM t1 WHERE a='1' AND b='2';
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+5
-2
@@ -847,6 +847,9 @@ foreach {tn x expr res nEval} {
|
||||
|
||||
3 5 "x() >= 5 AND x() <= 5" 1 2
|
||||
4 5 "x() BETWEEN 5 AND 5" 1 1
|
||||
|
||||
5 9 "(x(),8) >= (9,7) AND (x(),8)<=(9,10)" 1 2
|
||||
6 9 "(x(),8) BETWEEN (9,7) AND (9,10)" 1 1
|
||||
} {
|
||||
do_test e_expr-13.1.$tn {
|
||||
set ::xcount 0
|
||||
@@ -1809,7 +1812,7 @@ do_expr_test e_expr-35.1.6 {
|
||||
# The following block tests that errors are returned in a bunch of cases
|
||||
# where a subquery returns more than one column.
|
||||
#
|
||||
set M {only a single result allowed for a SELECT that is part of an expression}
|
||||
set M {/1 {sub-select returns [23] columns - expected 1}/}
|
||||
foreach {tn sql} {
|
||||
1 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
|
||||
2 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
|
||||
@@ -1818,7 +1821,7 @@ foreach {tn sql} {
|
||||
5 { SELECT (SELECT * FROM t2) }
|
||||
6 { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
|
||||
} {
|
||||
do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M]
|
||||
do_catchsql_test e_expr-35.2.$tn $sql $M
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-35764-28041 The result of the expression is the value
|
||||
|
||||
+1
-5
@@ -202,12 +202,8 @@ do_execsql_test e_vacuum-2.1.1 {
|
||||
} {}
|
||||
set original_size [file size test.db2]
|
||||
|
||||
# Try everything we can think of to get the aux database vacuumed:
|
||||
# Vacuuming the main database does not affect aux
|
||||
do_execsql_test e_vacuum-2.1.3 { VACUUM } {}
|
||||
do_execsql_test e_vacuum-2.1.4 { VACUUM aux } {}
|
||||
do_execsql_test e_vacuum-2.1.5 { VACUUM 'test.db2' } {}
|
||||
|
||||
# Despite our efforts, space in the aux database has not been reclaimed:
|
||||
do_test e_vacuum-2.1.6 { expr {[file size test.db2]==$::original_size} } 1
|
||||
|
||||
# EVIDENCE-OF: R-17495-17419 The VACUUM command may change the ROWIDs of
|
||||
|
||||
+8
-7
@@ -314,7 +314,7 @@ do_test in-9.4 {
|
||||
catchsql {
|
||||
SELECT b FROM t1 WHERE a NOT IN tb;
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
|
||||
# IN clauses in CHECK constraints. Ticket #1645
|
||||
#
|
||||
@@ -391,28 +391,28 @@ do_test in-12.2 {
|
||||
SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test in-12.3 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a, b FROM t3 UNION SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test in-12.4 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test in-12.5 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test in-12.6 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
@@ -478,7 +478,7 @@ do_test in-12.14 {
|
||||
SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test in-12.15 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
@@ -629,11 +629,12 @@ do_test in-13.14 {
|
||||
}
|
||||
} {}
|
||||
|
||||
breakpoint
|
||||
do_test in-13.15 {
|
||||
catchsql {
|
||||
SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
|
||||
|
||||
do_test in-13.X {
|
||||
|
||||
+12
-1
@@ -96,7 +96,18 @@ do_execsql_test limit2-210 {
|
||||
SELECT *, '|' FROM t200 LEFT JOIN t201 ON x=b ORDER BY y LIMIT 3;
|
||||
} {1 1 {} {} | 3 3 {} {} | 4 4 {} {} |}
|
||||
|
||||
|
||||
# Bug in the ORDER BY LIMIT optimization reported on 2016-09-06.
|
||||
# Ticket https://www.sqlite.org/src/info/559733b09e96
|
||||
#
|
||||
do_execsql_test limit2-300 {
|
||||
CREATE TABLE t300(a,b,c);
|
||||
CREATE INDEX t300x ON t300(a,b,c);
|
||||
INSERT INTO t300 VALUES(0,1,99),(0,1,0),(0,0,0);
|
||||
SELECT *,'.' FROM t300 WHERE a=0 AND (c=0 OR c=99) ORDER BY c DESC;
|
||||
} {0 1 99 . 0 0 0 . 0 1 0 .}
|
||||
do_execsql_test limit2-310 {
|
||||
SELECT *,'.' FROM t300 WHERE a=0 AND (c=0 OR c=99) ORDER BY c DESC LIMIT 1;
|
||||
} {0 1 99 .}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -108,4 +108,30 @@ do_execsql_test misc8-3.0 {
|
||||
ORDER BY 1;
|
||||
} {0 1 6 0 1 7}
|
||||
|
||||
# The SQLITE_DBCONFIG_MAINDBNAME interface
|
||||
#
|
||||
db close
|
||||
forcedelete test.db test2.db
|
||||
sqlite3 db test.db
|
||||
do_execsql_test misc8-4.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
ATTACH 'test2.db' AS aux2;
|
||||
CREATE TABLE aux2.t2(c,d,e);
|
||||
INSERT INTO t2 VALUES(4,5,6);
|
||||
SELECT * FROM t1, t2;
|
||||
} {1 2 3 4 5 6}
|
||||
do_execsql_test misc8-4.1 {
|
||||
PRAGMA database_list;
|
||||
} {/0 main .* 2 aux2/}
|
||||
dbconfig_maindbname_icecube db
|
||||
do_execsql_test misc8-4.2 {
|
||||
SELECT name FROM icecube.sqlite_master;
|
||||
} {t1}
|
||||
do_execsql_test misc8-4.3 {
|
||||
PRAGMA database_list;
|
||||
} {/0 icecube .* 2 aux2/}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
# 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 of this file is testing the SELECT statement.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue
|
||||
|
||||
do_execsql_test 0.0 {
|
||||
CREATE TABLE one(o);
|
||||
INSERT INTO one VALUES(1);
|
||||
}
|
||||
|
||||
foreach {tn v1 v2 eq ne is isnot} {
|
||||
1 "1, 2, 3" "1, 2, 3" 1 0 1 0
|
||||
2 "1, 0, 3" "1, 2, 3" 0 1 0 1
|
||||
3 "1, 2, NULL" "1, 2, 3" {} {} 0 1
|
||||
4 "1, 2, NULL" "1, 2, NULL" {} {} 1 0
|
||||
5 "NULL, NULL, NULL" "NULL, NULL, NULL" {} {} 1 0
|
||||
|
||||
6 "1, NULL, 1" "1, 1, 1" {} {} 0 1
|
||||
7 "1, NULL, 1" "1, 1, 2" 0 1 0 1
|
||||
} {
|
||||
do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq]
|
||||
do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne]
|
||||
|
||||
do_execsql_test 1.$tn.is "SELECT ($v1) IS ($v2)" [list $is]
|
||||
do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot]
|
||||
|
||||
do_execsql_test 1.$tn.2.eq "SELECT (SELECT $v1) == (SELECT $v2)" [list $eq]
|
||||
do_execsql_test 1.$tn.2.ne "SELECT (SELECT $v1) != (SELECT $v2)" [list $ne]
|
||||
}
|
||||
|
||||
foreach {tn v1 v2 lt gt le ge} {
|
||||
1 "(1, 1, 3)" "(1, 2, 3)" 1 0 1 0
|
||||
2 "(1, 2, 3)" "(1, 2, 3)" 0 0 1 1
|
||||
3 "(1, 3, 3)" "(1, 2, 3)" 0 1 0 1
|
||||
|
||||
4 "(1, NULL, 3)" "(1, 2, 3)" {} {} {} {}
|
||||
5 "(1, 3, 3)" "(1, NULL, 3)" {} {} {} {}
|
||||
6 "(1, NULL, 3)" "(1, NULL, 3)" {} {} {} {}
|
||||
} {
|
||||
foreach {tn2 expr res} [list \
|
||||
2.$tn.lt "$v1 < $v2" $lt \
|
||||
2.$tn.gt "$v1 > $v2" $gt \
|
||||
2.$tn.le "$v1 <= $v2" $le \
|
||||
2.$tn.ge "$v1 >= $v2" $ge \
|
||||
] {
|
||||
do_execsql_test $tn2 "SELECT $expr" [list $res]
|
||||
|
||||
set map(0) [list]
|
||||
set map() [list]
|
||||
set map(1) [list 1]
|
||||
do_execsql_test $tn2.where1 "SELECT * FROM one WHERE $expr" $map($res)
|
||||
|
||||
set map(0) [list 1]
|
||||
set map() [list]
|
||||
set map(1) [list]
|
||||
do_execsql_test $tn2.where2 "SELECT * FROM one WHERE NOT $expr" $map($res)
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(2, 3);
|
||||
INSERT INTO t1 VALUES(2, 4);
|
||||
INSERT INTO t1 VALUES(3, 5);
|
||||
INSERT INTO t1 VALUES(3, 6);
|
||||
}
|
||||
|
||||
foreach {tn r order} {
|
||||
1 "(1, 1)" "ORDER BY y"
|
||||
2 "(1, 1)" "ORDER BY x, y"
|
||||
3 "(1, 2)" "ORDER BY x, y DESC"
|
||||
4 "(3, 6)" "ORDER BY x DESC, y DESC"
|
||||
5 "((3, 5))" "ORDER BY x DESC, y"
|
||||
6 "(SELECT 3, 5)" "ORDER BY x DESC, y"
|
||||
} {
|
||||
do_execsql_test 3.$tn.1 "SELECT $r == (SELECT x,y FROM t1 $order)" 1
|
||||
do_execsql_test 3.$tn.2 "SELECT $r == (SELECT * FROM t1 $order)" 1
|
||||
|
||||
do_execsql_test 3.$tn.3 "
|
||||
SELECT (SELECT * FROM t1 $order) == (SELECT * FROM t1 $order)
|
||||
" 1
|
||||
do_execsql_test 3.$tn.4 "
|
||||
SELECT (SELECT 0, 0) == (SELECT * FROM t1 $order)
|
||||
" 0
|
||||
}
|
||||
|
||||
foreach {tn expr res} {
|
||||
1 {(2, 2) BETWEEN (2, 2) AND (3, 3)} 1
|
||||
2 {(2, 2) BETWEEN (2, NULL) AND (3, 3)} {}
|
||||
3 {(2, 2) BETWEEN (3, NULL) AND (3, 3)} 0
|
||||
} {
|
||||
do_execsql_test 4.$tn "SELECT $expr" [list $res]
|
||||
}
|
||||
|
||||
foreach {tn expr res} {
|
||||
1 {(2, 4) IN (SELECT * FROM t1)} 1
|
||||
2 {(3, 4) IN (SELECT * FROM t1)} 0
|
||||
|
||||
3 {(NULL, 4) IN (SELECT * FROM t1)} {}
|
||||
4 {(NULL, 0) IN (SELECT * FROM t1)} 0
|
||||
|
||||
5 {(NULL, 4) NOT IN (SELECT * FROM t1)} {}
|
||||
6 {(NULL, 0) NOT IN (SELECT * FROM t1)} 1
|
||||
} {
|
||||
do_execsql_test 5.$tn "SELECT $expr" [list $res]
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE hh(a, b, c);
|
||||
INSERT INTO hh VALUES('abc', 1, 'i');
|
||||
INSERT INTO hh VALUES('ABC', 1, 'ii');
|
||||
INSERT INTO hh VALUES('def', 2, 'iii');
|
||||
INSERT INTO hh VALUES('DEF', 2, 'iv');
|
||||
INSERT INTO hh VALUES('GHI', 3, 'v');
|
||||
INSERT INTO hh VALUES('ghi', 3, 'vi');
|
||||
|
||||
CREATE INDEX hh_ab ON hh(a, b);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1);
|
||||
} {i}
|
||||
do_execsql_test 6.2 {
|
||||
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc' COLLATE nocase, 1);
|
||||
} {i}
|
||||
do_execsql_test 6.3 {
|
||||
SELECT c FROM hh WHERE a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1);
|
||||
} {i}
|
||||
do_execsql_test 6.4 {
|
||||
SELECT c FROM hh WHERE +a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1);
|
||||
} {i}
|
||||
do_execsql_test 6.5 {
|
||||
SELECT c FROM hh WHERE a = (SELECT 'abc') COLLATE nocase AND b = (SELECT 1);
|
||||
} {i ii}
|
||||
do_catchsql_test 6.6 {
|
||||
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1) COLLATE nocase;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 6.7 {
|
||||
SELECT c FROM hh WHERE (a, b) = 1;
|
||||
} {1 {row value misused}}
|
||||
do_execsql_test 6.8 {
|
||||
SELECT c FROM hh WHERE (a COLLATE nocase, b) = (SELECT 'def', 2);
|
||||
} {iii iv}
|
||||
do_execsql_test 6.9 {
|
||||
SELECT c FROM hh WHERE (a COLLATE nocase, b) IS NOT (SELECT 'def', 2);
|
||||
} {i ii v vi}
|
||||
do_execsql_test 6.10 {
|
||||
SELECT c FROM hh WHERE (b, a) = (SELECT 2, 'def');
|
||||
} {iii}
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE xy(i INTEGER PRIMARY KEY, j, k);
|
||||
INSERT INTO xy VALUES(1, 1, 1);
|
||||
INSERT INTO xy VALUES(2, 2, 2);
|
||||
INSERT INTO xy VALUES(3, 3, 3);
|
||||
INSERT INTO xy VALUES(4, 4, 4);
|
||||
}
|
||||
|
||||
|
||||
foreach {tn sql res eqp} {
|
||||
1 "SELECT * FROM xy WHERE (i, j) IS (2, 2)" {2 2 2}
|
||||
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid=?)}"
|
||||
|
||||
2 "SELECT * FROM xy WHERE (k, j) < (2, 3)" {1 1 1 2 2 2}
|
||||
"0 0 0 {SCAN TABLE xy}"
|
||||
|
||||
3 "SELECT * FROM xy WHERE (i, j) < (2, 3)" {1 1 1 2 2 2}
|
||||
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid<?)}"
|
||||
|
||||
4 "SELECT * FROM xy WHERE (i, j) > (2, 1)" {2 2 2 3 3 3 4 4 4}
|
||||
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}"
|
||||
|
||||
5 "SELECT * FROM xy WHERE (i, j) > ('2', 1)" {2 2 2 3 3 3 4 4 4}
|
||||
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}"
|
||||
|
||||
} {
|
||||
do_eqp_test 7.$tn.1 $sql $eqp
|
||||
do_execsql_test 7.$tn.2 $sql $res
|
||||
}
|
||||
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE j1(a);
|
||||
}
|
||||
do_execsql_test 8.1 {
|
||||
SELECT * FROM j1 WHERE (select min(a) FROM j1) IN (?, ?, ?)
|
||||
}
|
||||
|
||||
do_execsql_test 9.0 {
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t2 VALUES(1, 1, 1);
|
||||
INSERT INTO t2 VALUES(2, 2, 2);
|
||||
INSERT INTO t2 VALUES(3, 3, 3);
|
||||
INSERT INTO t2 VALUES(4, 4, 4);
|
||||
INSERT INTO t2 VALUES(5, 5, 5);
|
||||
}
|
||||
|
||||
foreach {tn q res} {
|
||||
1 "(a, b) > (2, 1)" {2 3 4 5}
|
||||
2 "(a, b) > (2, 2)" {3 4 5}
|
||||
3 "(a, b) < (4, 5)" {1 2 3 4}
|
||||
4 "(a, b) < (4, 3)" {1 2 3}
|
||||
} {
|
||||
do_execsql_test 9.$tn "SELECT c FROM t2 WHERE $q" $res
|
||||
}
|
||||
|
||||
do_execsql_test 10.0 {
|
||||
CREATE TABLE dual(dummy); INSERT INTO dual(dummy) VALUES('X');
|
||||
CREATE TABLE t3(a TEXT,b TEXT,c TEXT,d TEXT,e TEXT,f TEXT);
|
||||
CREATE INDEX t3x ON t3(b,c,d,e,f);
|
||||
|
||||
SELECT a FROM t3
|
||||
WHERE (c,d) IN (SELECT 'c','d' FROM dual)
|
||||
AND (a,b,e) IN (SELECT 'a','b','d' FROM dual);
|
||||
}
|
||||
|
||||
do_catchsql_test 11.1 {
|
||||
CREATE TABLE t11(a);
|
||||
SELECT * FROM t11 WHERE (a,a)<=1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.2 {
|
||||
SELECT * FROM t11 WHERE (a,a)<1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.3 {
|
||||
SELECT * FROM t11 WHERE (a,a)>=1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.4 {
|
||||
SELECT * FROM t11 WHERE (a,a)>1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.5 {
|
||||
SELECT * FROM t11 WHERE (a,a)==1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.6 {
|
||||
SELECT * FROM t11 WHERE (a,a)<>1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.7 {
|
||||
SELECT * FROM t11 WHERE (a,a) IS 1;
|
||||
} {1 {row value misused}}
|
||||
do_catchsql_test 11.8 {
|
||||
SELECT * FROM t11 WHERE (a,a) IS NOT 1;
|
||||
} {1 {row value misused}}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,279 @@
|
||||
# 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 of this file is testing the SELECT statement.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(0, 0, 0);
|
||||
INSERT INTO t1 VALUES(0, 1, 1);
|
||||
INSERT INTO t1 VALUES(1, 0, 2);
|
||||
INSERT INTO t1 VALUES(1, 1, 3);
|
||||
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 { SELECT c FROM t1 WHERE (a, b) >= (1, 0) } {2 3}
|
||||
do_execsql_test 1.1.2 { SELECT c FROM t1 WHERE (a, b) > (1, 0) } {3}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 2.0.1 {
|
||||
CREATE TABLE t2(a INTEGER, b INTEGER, c INTEGER, d INTEGER);
|
||||
CREATE INDEX i2 ON t2(a, b, c);
|
||||
}
|
||||
do_test 2.0.2 {
|
||||
foreach a {0 1 2 3} {
|
||||
foreach b {0 1 2 3} {
|
||||
foreach c {0 1 2 3} {
|
||||
execsql { INSERT INTO t2 VALUES($a, $b, $c, $c + $b*4 + $a*16); }
|
||||
}}}
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT d FROM t2 WHERE (a, b) > (2, 2);
|
||||
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>2) }]
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT d FROM t2 WHERE (a, b) >= (2, 2);
|
||||
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>=2) }]
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT d FROM t2 WHERE a=1 AND (b, c) >= (1, 2);
|
||||
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>=2)) }]
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
SELECT d FROM t2 WHERE a=1 AND (b, c) > (1, 2);
|
||||
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>2)) }]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
set words {
|
||||
airfare airfield airfields airflow airfoil
|
||||
airfoils airframe airframes airily airing
|
||||
airings airless airlift airlifts airline
|
||||
airliner airlines airlock airlocks airmail
|
||||
airmails airman airmen airplane airplanes
|
||||
|
||||
arraignment arraignments arraigns arrange arranged
|
||||
arrangement arrangements arranger arrangers arranges
|
||||
arranging arrant array arrayed arrays
|
||||
arrears arrest arrested arrester arresters
|
||||
arresting arrestingly arrestor arrestors arrests
|
||||
|
||||
edifices edit edited editing edition
|
||||
editions editor editorial editorially editorials
|
||||
editors edits educable educate educated
|
||||
educates educating education educational educationally
|
||||
educations educator educators eel eelgrass
|
||||
}
|
||||
|
||||
do_test 3.0 {
|
||||
execsql { CREATE TABLE t3(a, b, c, w); }
|
||||
foreach w $words {
|
||||
set a [string range $w 0 2]
|
||||
set b [string range $w 3 5]
|
||||
set c [string range $w 6 end]
|
||||
execsql { INSERT INTO t3 VALUES($a, $b, $c, $w) }
|
||||
}
|
||||
} {}
|
||||
|
||||
|
||||
foreach {tn idx} {
|
||||
IDX1 {}
|
||||
IDX2 { CREATE INDEX i3 ON t3(a, b, c); }
|
||||
IDX3 { CREATE INDEX i3 ON t3(a, b); }
|
||||
IDX4 { CREATE INDEX i3 ON t3(a); }
|
||||
} {
|
||||
execsql { DROP INDEX IF EXISTS i3 }
|
||||
execsql $idx
|
||||
|
||||
foreach w $words {
|
||||
set a [string range $w 0 2]
|
||||
set b [string range $w 3 5]
|
||||
set c [string range $w 6 end]
|
||||
|
||||
foreach op [list > >= < <= == IS] {
|
||||
do_execsql_test 3.1.$tn.$w.$op [subst -novar {
|
||||
SELECT rowid FROM t3 WHERE (a, b, c) [set op] ($a, $b, $c)
|
||||
ORDER BY +rowid
|
||||
}] [db eval [subst -novar {
|
||||
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
|
||||
}]]
|
||||
|
||||
do_execsql_test 3.1.$tn.$w.$op.subselect [subst -novar {
|
||||
SELECT rowid FROM t3 WHERE (a, b, c) [set op] (
|
||||
SELECT a, b, c FROM t3 WHERE w = $w
|
||||
)
|
||||
ORDER BY +rowid
|
||||
}] [db eval [subst -novar {
|
||||
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
|
||||
}]]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t4(a, b, c);
|
||||
INSERT INTO t4 VALUES(NULL, NULL, NULL);
|
||||
INSERT INTO t4 VALUES(NULL, NULL, 0);
|
||||
INSERT INTO t4 VALUES(NULL, NULL, 1);
|
||||
INSERT INTO t4 VALUES(NULL, 0, NULL);
|
||||
INSERT INTO t4 VALUES(NULL, 0, 0);
|
||||
INSERT INTO t4 VALUES(NULL, 0, 1);
|
||||
INSERT INTO t4 VALUES(NULL, 1, NULL);
|
||||
INSERT INTO t4 VALUES(NULL, 1, 0);
|
||||
INSERT INTO t4 VALUES(NULL, 1, 1);
|
||||
|
||||
INSERT INTO t4 VALUES( 0, NULL, NULL);
|
||||
INSERT INTO t4 VALUES( 0, NULL, 0);
|
||||
INSERT INTO t4 VALUES( 0, NULL, 1);
|
||||
INSERT INTO t4 VALUES( 0, 0, NULL);
|
||||
INSERT INTO t4 VALUES( 0, 0, 0);
|
||||
INSERT INTO t4 VALUES( 0, 0, 1);
|
||||
INSERT INTO t4 VALUES( 0, 1, NULL);
|
||||
INSERT INTO t4 VALUES( 0, 1, 0);
|
||||
INSERT INTO t4 VALUES( 0, 1, 1);
|
||||
|
||||
INSERT INTO t4 VALUES( 1, NULL, NULL);
|
||||
INSERT INTO t4 VALUES( 1, NULL, 0);
|
||||
INSERT INTO t4 VALUES( 1, NULL, 1);
|
||||
INSERT INTO t4 VALUES( 1, 0, NULL);
|
||||
INSERT INTO t4 VALUES( 1, 0, 0);
|
||||
INSERT INTO t4 VALUES( 1, 0, 1);
|
||||
INSERT INTO t4 VALUES( 1, 1, NULL);
|
||||
INSERT INTO t4 VALUES( 1, 1, 0);
|
||||
INSERT INTO t4 VALUES( 1, 1, 1);
|
||||
}
|
||||
|
||||
proc make_expr1 {cList vList op} {
|
||||
return "([join $cList ,]) $op ([join $vList ,])"
|
||||
}
|
||||
|
||||
proc make_expr3 {cList vList op} {
|
||||
set n [llength $cList]
|
||||
|
||||
set aList [list]
|
||||
foreach c [lrange $cList 0 end-1] v [lrange $vList 0 end-1] {
|
||||
lappend aList "$c == $v"
|
||||
}
|
||||
lappend aList "[lindex $cList end] $op [lindex $vList end]"
|
||||
|
||||
return "([join $aList { AND }])"
|
||||
}
|
||||
|
||||
proc make_expr2 {cList vList op} {
|
||||
set ret ""
|
||||
|
||||
switch -- $op {
|
||||
== - IS {
|
||||
set aList [list]
|
||||
foreach c $cList v $vList { lappend aList "($c $op $v)" }
|
||||
set ret [join $aList " AND "]
|
||||
}
|
||||
|
||||
< - > {
|
||||
set oList [list]
|
||||
for {set i 0} {$i < [llength $cList]} {incr i} {
|
||||
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $op]
|
||||
}
|
||||
set ret [join $oList " OR "]
|
||||
}
|
||||
|
||||
<= - >= {
|
||||
set o2 [string range $op 0 0]
|
||||
set oList [list]
|
||||
for {set i 0} {$i < [llength $cList]-1} {incr i} {
|
||||
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $o2]
|
||||
}
|
||||
lappend oList [make_expr3 $cList $vList $op]
|
||||
set ret [join $oList " OR "]
|
||||
}
|
||||
|
||||
|
||||
default {
|
||||
error "Unknown op: $op"
|
||||
}
|
||||
}
|
||||
|
||||
set ret
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
IDX1 {}
|
||||
IDX2 { CREATE INDEX i4 ON t4(a, b, c); }
|
||||
IDX3 { CREATE INDEX i4 ON t4(a, b); }
|
||||
IDX4 { CREATE INDEX i4 ON t4(a); }
|
||||
} {
|
||||
execsql { DROP INDEX IF EXISTS i4 }
|
||||
execsql $idx
|
||||
|
||||
foreach {tn2 vector} {
|
||||
1 {0 0 0}
|
||||
2 {1 1 1}
|
||||
3 {0 0 NULL}
|
||||
4 {0 NULL 0}
|
||||
5 {NULL 0 0}
|
||||
6 {1 1 NULL}
|
||||
7 {1 NULL 1}
|
||||
8 {NULL 1 1}
|
||||
} {
|
||||
foreach op { IS == < <= > >= } {
|
||||
set e1 [make_expr1 {a b c} $vector $op]
|
||||
set e2 [make_expr2 {a b c} $vector $op]
|
||||
|
||||
do_execsql_test 4.$tn.$tn2.$op \
|
||||
"SELECT rowid FROM t4 WHERE $e2 ORDER BY +rowid" [
|
||||
db eval "SELECT rowid FROM t4 WHERE $e1 ORDER BY +rowid"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE r1(a TEXT, iB TEXT);
|
||||
CREATE TABLE r2(x TEXT, zY INTEGER);
|
||||
CREATE INDEX r1ab ON r1(a, iB);
|
||||
|
||||
INSERT INTO r1 VALUES(35, 35);
|
||||
INSERT INTO r2 VALUES(35, 36);
|
||||
INSERT INTO r2 VALUES(35, 4);
|
||||
INSERT INTO r2 VALUES(35, 35);
|
||||
} {}
|
||||
|
||||
foreach {tn lhs rhs} {
|
||||
1 {x +zY} {a iB}
|
||||
2 {x zY} {a iB}
|
||||
3 {x zY} {a +iB}
|
||||
4 {+x zY} {a iB}
|
||||
5 {x zY} {+a iB}
|
||||
} {
|
||||
foreach op { IS == < <= > >= } {
|
||||
set e1 [make_expr1 $lhs $rhs $op]
|
||||
set e2 [make_expr2 $lhs $rhs $op]
|
||||
do_execsql_test 5.$tn.$op \
|
||||
"SELECT * FROM r1, r2 WHERE $e2 ORDER BY iB" [db eval \
|
||||
"SELECT * FROM r1, r2 WHERE $e1 ORDER BY iB"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,206 @@
|
||||
# 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 of this file is testing "(...) IN (SELECT ...)" expressions
|
||||
# where the SELECT statement returns more than one column.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue3
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
}
|
||||
|
||||
foreach {tn sql res} {
|
||||
1 "SELECT 1 WHERE (4, 5) IN (SELECT a, b FROM t1)" 1
|
||||
2 "SELECT 1 WHERE (5, 5) IN (SELECT a, b FROM t1)" {}
|
||||
3 "SELECT 1 WHERE (5, 4) IN (SELECT a, b FROM t1)" {}
|
||||
4 "SELECT 1 WHERE (5, 4) IN (SELECT b, a FROM t1)" 1
|
||||
5 "SELECT 1 WHERE (SELECT a, b FROM t1 WHERE c=6) IN (SELECT a, b FROM t1)" 1
|
||||
6 "SELECT (5, 4) IN (SELECT a, b FROM t1)" 0
|
||||
7 "SELECT 1 WHERE (5, 4) IN (SELECT +b, +a FROM t1)" 1
|
||||
8 "SELECT (5, 4) IN (SELECT +b, +a FROM t1)" 1
|
||||
9 "SELECT (1, 2) IN (SELECT rowid, b FROM t1)" 1
|
||||
10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)" 1
|
||||
11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)" {}
|
||||
12 "SELECT 1 FROM t1 WHERE (a, b) = (SELECT +a, +b FROM t1)" {1}
|
||||
} {
|
||||
do_execsql_test 1.$tn $sql $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE z1(x, y, z);
|
||||
CREATE TABLE kk(a, b);
|
||||
|
||||
INSERT INTO z1 VALUES('a', 'b', 'c');
|
||||
INSERT INTO z1 VALUES('d', 'e', 'f');
|
||||
INSERT INTO z1 VALUES('g', 'h', 'i');
|
||||
|
||||
-- INSERT INTO kk VALUES('y', 'y');
|
||||
INSERT INTO kk VALUES('d', 'e');
|
||||
-- INSERT INTO kk VALUES('x', 'x');
|
||||
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
1 { }
|
||||
2 { CREATE INDEX z1idx ON z1(x, y) }
|
||||
3 { CREATE UNIQUE INDEX z1idx ON z1(x, y) }
|
||||
4 { CREATE INDEX z1idx ON kk(a, b) }
|
||||
} {
|
||||
execsql "DROP INDEX IF EXISTS z1idx"
|
||||
execsql $idx
|
||||
|
||||
do_execsql_test 2.$tn.1 {
|
||||
SELECT * FROM z1 WHERE x IN (SELECT a FROM kk)
|
||||
} {d e f}
|
||||
|
||||
do_execsql_test 2.$tn.2 {
|
||||
SELECT * FROM z1 WHERE (x,y) IN (SELECT a, b FROM kk)
|
||||
} {d e f}
|
||||
|
||||
do_execsql_test 2.$tn.3 {
|
||||
SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b FROM kk)
|
||||
} {d e f}
|
||||
|
||||
do_execsql_test 2.$tn.4 {
|
||||
SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b||'x' FROM kk)
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.$tn.5 {
|
||||
SELECT * FROM z1 WHERE (+x, y) IN (SELECT a, b FROM kk)
|
||||
} {d e f}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE c1(a, b, c, d);
|
||||
INSERT INTO c1(rowid, a, b) VALUES(1, NULL, 1);
|
||||
INSERT INTO c1(rowid, a, b) VALUES(2, 2, NULL);
|
||||
INSERT INTO c1(rowid, a, b) VALUES(3, 2, 2);
|
||||
INSERT INTO c1(rowid, a, b) VALUES(4, 3, 3);
|
||||
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(101, 'a', 'b', 1, 1);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(102, 'a', 'b', 1, 2);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(103, 'a', 'b', 1, 3);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(104, 'a', 'b', 2, 1);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(105, 'a', 'b', 2, 2);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(106, 'a', 'b', 2, 3);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(107, 'a', 'b', 3, 1);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(108, 'a', 'b', 3, 2);
|
||||
INSERT INTO c1(rowid, a, b, c, d) VALUES(109, 'a', 'b', 3, 3);
|
||||
}
|
||||
|
||||
|
||||
foreach {tn idx} {
|
||||
1 { }
|
||||
2 { CREATE INDEX c1ab ON c1(a, b); }
|
||||
3 { CREATE INDEX c1ba ON c1(b, a); }
|
||||
|
||||
4 { CREATE INDEX c1cd ON c1(c, d); }
|
||||
5 { CREATE INDEX c1dc ON c1(d, c); }
|
||||
} {
|
||||
drop_all_indexes
|
||||
|
||||
foreach {tn2 sql res} {
|
||||
1 "SELECT (1, 2) IN (SELECT a, b FROM c1)" {0}
|
||||
2 "SELECT (1, 1) IN (SELECT a, b FROM c1)" {{}}
|
||||
3 "SELECT (2, 1) IN (SELECT a, b FROM c1)" {{}}
|
||||
4 "SELECT (2, 2) IN (SELECT a, b FROM c1)" {1}
|
||||
5 "SELECT c, d FROM c1 WHERE (c, d) IN (SELECT d, c FROM c1)"
|
||||
{ 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 }
|
||||
|
||||
6 "SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1) ORDER BY c DESC"
|
||||
{ 3 1 3 2 3 3 2 1 2 2 2 3 1 1 1 2 1 3 }
|
||||
|
||||
7 {
|
||||
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
|
||||
ORDER BY c DESC, d ASC
|
||||
} { 3 1 3 2 3 3 2 1 2 2 2 3 1 1 1 2 1 3 }
|
||||
|
||||
8 {
|
||||
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
|
||||
ORDER BY c ASC, d DESC
|
||||
} { 1 3 1 2 1 1 2 3 2 2 2 1 3 3 3 2 3 1 }
|
||||
|
||||
9 {
|
||||
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
|
||||
ORDER BY c ASC, d ASC
|
||||
} { 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 }
|
||||
10 {
|
||||
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
|
||||
ORDER BY c DESC, d DESC
|
||||
} { 3 3 3 2 3 1 2 3 2 2 2 1 1 3 1 2 1 1 }
|
||||
|
||||
} {
|
||||
do_execsql_test 3.$tn.$tn2 $sql $res
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE hh(a, b, c);
|
||||
|
||||
INSERT INTO hh VALUES('a', 'a', 1);
|
||||
INSERT INTO hh VALUES('a', 'b', 2);
|
||||
INSERT INTO hh VALUES('b', 'a', 3);
|
||||
INSERT INTO hh VALUES('b', 'b', 4);
|
||||
|
||||
CREATE TABLE k1(x, y);
|
||||
INSERT INTO k1 VALUES('a', 'a');
|
||||
INSERT INTO k1 VALUES('b', 'b');
|
||||
INSERT INTO k1 VALUES('a', 'b');
|
||||
INSERT INTO k1 VALUES('b', 'a');
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
1 { }
|
||||
2 { CREATE INDEX h1 ON hh(a, b); }
|
||||
3 { CREATE UNIQUE INDEX k1idx ON k1(x, y) }
|
||||
4 { CREATE UNIQUE INDEX k1idx ON k1(x, y DESC) }
|
||||
5 {
|
||||
CREATE INDEX h1 ON hh(a, b);
|
||||
CREATE UNIQUE INDEX k1idx ON k1(x, y);
|
||||
}
|
||||
6 {
|
||||
CREATE INDEX h1 ON hh(a, b);
|
||||
CREATE UNIQUE INDEX k1idx ON k1(x, y DESC);
|
||||
}
|
||||
} {
|
||||
drop_all_indexes
|
||||
execsql $idx
|
||||
foreach {tn2 orderby res} {
|
||||
1 "a ASC, b ASC" {1 2 3 4}
|
||||
2 "a ASC, b DESC" {2 1 4 3}
|
||||
3 "a DESC, b ASC" {3 4 1 2}
|
||||
4 "a DESC, b DESC" {4 3 2 1}
|
||||
} {
|
||||
do_execsql_test 4.$tn.$tn2 "
|
||||
SELECT c FROM hh WHERE (a, b) in (SELECT x, y FROM k1) ORDER BY $orderby
|
||||
" $res
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,313 @@
|
||||
# 2016 July 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 implements regression tests for SQLite library. The
|
||||
# focus of this file is syntax errors involving row-value constructors
|
||||
# and sub-selects that return multiple arguments.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue4
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some error conditions:
|
||||
#
|
||||
# * row values used where they are not supported,
|
||||
# * row values or sub-selects that contain/return the wrong number
|
||||
# of elements.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX t1bac ON t1(b, a, c);
|
||||
}
|
||||
|
||||
foreach {tn e} {
|
||||
1 "(1, 2, 3)"
|
||||
2 "1 + (1, 2)"
|
||||
3 "(1,2,3) == (1, 2)"
|
||||
} {
|
||||
do_catchsql_test 1.$tn "SELECT $e" {1 {row value misused}}
|
||||
}
|
||||
|
||||
foreach {tn s error} {
|
||||
1 "SELECT * FROM t1 WHERE a = (1, 2)" {row value misused}
|
||||
2 "SELECT * FROM t1 WHERE b = (1, 2)" {row value misused}
|
||||
3 "SELECT * FROM t1 WHERE NOT (b = (1, 2))" {row value misused}
|
||||
4 "SELECT * FROM t1 LIMIT (1, 2)" {row value misused}
|
||||
5 "SELECT (a, b) IN (SELECT * FROM t1) FROM t1"
|
||||
{sub-select returns 3 columns - expected 2}
|
||||
|
||||
6 "SELECT * FROM t1 WHERE (a, b) IN (SELECT * FROM t1)"
|
||||
{sub-select returns 3 columns - expected 2}
|
||||
7 "SELECT * FROM t1 WHERE (c, c) <= 1" {row value misused}
|
||||
8 "SELECT * FROM t1 WHERE (b, b) <= 1" {row value misused}
|
||||
} {
|
||||
do_catchsql_test 2.$tn "$s" [list 1 $error]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t2(a, b, c, d);
|
||||
INSERT INTO t2 VALUES(1, 1, 1, 1);
|
||||
INSERT INTO t2 VALUES(1, 1, 2, 2);
|
||||
INSERT INTO t2 VALUES(1, 1, 3, 3);
|
||||
INSERT INTO t2 VALUES(1, 2, 1, 4);
|
||||
INSERT INTO t2 VALUES(1, 2, 2, 5);
|
||||
INSERT INTO t2 VALUES(1, 2, 3, 6);
|
||||
INSERT INTO t2 VALUES(1, 3, 1, 7);
|
||||
INSERT INTO t2 VALUES(1, 3, 2, 8);
|
||||
INSERT INTO t2 VALUES(1, 3, 3, 9);
|
||||
|
||||
INSERT INTO t2 VALUES(2, 1, 1, 10);
|
||||
INSERT INTO t2 VALUES(2, 1, 2, 11);
|
||||
INSERT INTO t2 VALUES(2, 1, 3, 12);
|
||||
INSERT INTO t2 VALUES(2, 2, 1, 13);
|
||||
INSERT INTO t2 VALUES(2, 2, 2, 14);
|
||||
INSERT INTO t2 VALUES(2, 2, 3, 15);
|
||||
INSERT INTO t2 VALUES(2, 3, 1, 16);
|
||||
INSERT INTO t2 VALUES(2, 3, 2, 17);
|
||||
INSERT INTO t2 VALUES(2, 3, 3, 18);
|
||||
|
||||
INSERT INTO t2 VALUES(3, 1, 1, 19);
|
||||
INSERT INTO t2 VALUES(3, 1, 2, 20);
|
||||
INSERT INTO t2 VALUES(3, 1, 3, 21);
|
||||
INSERT INTO t2 VALUES(3, 2, 1, 22);
|
||||
INSERT INTO t2 VALUES(3, 2, 2, 23);
|
||||
INSERT INTO t2 VALUES(3, 2, 3, 24);
|
||||
INSERT INTO t2 VALUES(3, 3, 1, 25);
|
||||
INSERT INTO t2 VALUES(3, 3, 2, 26);
|
||||
INSERT INTO t2 VALUES(3, 3, 3, 27);
|
||||
}
|
||||
|
||||
foreach {nm idx} {
|
||||
idx1 {}
|
||||
idx2 { CREATE INDEX t2abc ON t2(a, b, c); }
|
||||
idx3 { CREATE INDEX t2abc ON t2(a, b DESC, c); }
|
||||
idx4 { CREATE INDEX t2abc ON t2(a DESC, b DESC, c DESC); }
|
||||
idx5 { CREATE INDEX t2abc ON t2(a ASC, b ASC, c ASC); }
|
||||
idx6 { CREATE INDEX t2abc ON t2(a DESC, b, c); }
|
||||
idx7 { CREATE INDEX t2abc ON t2(a DESC, b DESC) }
|
||||
idx8 { CREATE INDEX t2abc ON t2(c, b, a); }
|
||||
idx9 { CREATE INDEX t2d ON t2(d); }
|
||||
idx10 { CREATE INDEX t2abc ON t2(a DESC, b, c DESC); }
|
||||
} {
|
||||
drop_all_indexes
|
||||
execsql $idx
|
||||
|
||||
foreach {tn where res} {
|
||||
1 "(a, b, c) < (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
2 "(a, b, c) <= (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13 14}
|
||||
3 "(a, b, c) > (2, 2, 2)" {15 16 17 18 19 20 21 22 23 24 25 26 27}
|
||||
4 "(a, b, c) >= (2, 2, 2)" {14 15 16 17 18 19 20 21 22 23 24 25 26 27}
|
||||
5 "(a, b, c) >= (2, 2, NULL)" {16 17 18 19 20 21 22 23 24 25 26 27}
|
||||
6 "(a, b, c) <= (2, 2, NULL)" {1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
7 "(a, b, c) >= (2, NULL, NULL)" {19 20 21 22 23 24 25 26 27}
|
||||
8 "(a, b, c) <= (2, NULL, NULL)" {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
9 "(a, b, c) < (SELECT a, b, c FROM t2 WHERE d=14)"
|
||||
{1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
|
||||
10 "(a, b, c) = (SELECT a, b, c FROM t2 WHERE d=14)" 14
|
||||
|
||||
11 "a = 2 AND (b, c) > (2, 2)" {15 16 17 18}
|
||||
12 "a = 2 AND (b, c) < (3, 3) AND (b, c) > (1, 1)" {11 12 13 14 15 16 17}
|
||||
} {
|
||||
set result [db eval "SELECT d FROM t2 WHERE $where"]
|
||||
do_test 2.1.$nm.$tn { lsort -integer $result } $res
|
||||
}
|
||||
|
||||
foreach {tn e res} {
|
||||
1 "(2, 1) IN (SELECT a, b FROM t2)" 1
|
||||
2 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d)" 1
|
||||
3 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 9)" 0
|
||||
4 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 10)" 1
|
||||
|
||||
5 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d DESC LIMIT 1)" 1
|
||||
6 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" 0
|
||||
7 "(1, NULL) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" {{}}
|
||||
|
||||
8 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d DESC LIMIT 1 OFFSET 2)" 1
|
||||
9 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" 0
|
||||
10 "(1, NULL) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" {{}}
|
||||
|
||||
11 "(3, 3) = (SELECT max(a), max(b) FROM t2)" 1
|
||||
12 "(3, 1) = (SELECT max(a), min(b) FROM t2)" 1
|
||||
13 "(NULL, NULL) = (SELECT max(a), min(b) FROM t2)" {{}}
|
||||
|
||||
14 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 11)" 1
|
||||
15 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 12)" 0
|
||||
} {
|
||||
do_execsql_test 2.2.$nm.$tn "SELECT $e" $res
|
||||
}
|
||||
}
|
||||
|
||||
ifcapable stat4 {
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE c1(a, b, c, d);
|
||||
INSERT INTO c1(a, b) VALUES(1, 'a');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'b');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'c');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'd');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'e');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'f');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'g');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'h');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'i');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'j');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'k');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'l');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'm');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'n');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'o');
|
||||
INSERT INTO c1(a, b) VALUES(1, 'p');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'a');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'b');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'c');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'd');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'e');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'f');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'g');
|
||||
INSERT INTO c1(a, b) VALUES(2, 'h');
|
||||
|
||||
INSERT INTO c1(c, d) SELECT a, b FROM c1;
|
||||
|
||||
CREATE INDEX c1ab ON c1(a, b);
|
||||
CREATE INDEX c1cd ON c1(c, d);
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_eqp_test 3.1.1 { SELECT * FROM c1 WHERE a=1 AND c=2 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)}
|
||||
}
|
||||
do_eqp_test 3.1.2 { SELECT * FROM c1 WHERE a=1 AND b>'d' AND c=2 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)}
|
||||
}
|
||||
do_eqp_test 3.1.3 { SELECT * FROM c1 WHERE a=1 AND b>'l' AND c=2 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=? AND b>?)}
|
||||
}
|
||||
|
||||
do_eqp_test 3.2.1 { SELECT * FROM c1 WHERE a=1 AND c>1 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c>?)}
|
||||
}
|
||||
do_eqp_test 3.2.2 { SELECT * FROM c1 WHERE a=1 AND c>0 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
|
||||
}
|
||||
do_eqp_test 3.2.3 { SELECT * FROM c1 WHERE a=1 AND c>=1 } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
|
||||
}
|
||||
do_eqp_test 3.2.4 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'c') } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
|
||||
}
|
||||
do_eqp_test 3.2.5 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'o') } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd ((c,d)>(?,?))}
|
||||
}
|
||||
do_eqp_test 3.2.6 { SELECT * FROM c1 WHERE a=1 AND (c, +b)>(1, 'c') } {
|
||||
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
|
||||
}
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE d1(x, y);
|
||||
CREATE TABLE d2(a, b, c);
|
||||
CREATE INDEX d2ab ON d2(a, b);
|
||||
CREATE INDEX d2c ON d2(c);
|
||||
|
||||
WITH i(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM i WHERE i<1000
|
||||
)
|
||||
INSERT INTO d2 SELECT i/3, i%3, i/3 FROM i;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_eqp_test 5.1 {
|
||||
SELECT * FROM d2 WHERE
|
||||
(a, b) IN (SELECT x, y FROM d1) AND
|
||||
(c) IN (SELECT y FROM d1)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE d2 USING INDEX d2ab (a=? AND b=?)}
|
||||
0 0 0 {EXECUTE LIST SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE d1}
|
||||
0 0 0 {EXECUTE LIST SUBQUERY 2}
|
||||
2 0 0 {SCAN TABLE d1}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE e1(a, b, c, d, e);
|
||||
CREATE INDEX e1ab ON e1(a, b);
|
||||
CREATE INDEX e1cde ON e1(c, d, e);
|
||||
}
|
||||
|
||||
do_eqp_test 6.1 {
|
||||
SELECT * FROM e1 WHERE (a, b) > (?, ?)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)>(?,?))}
|
||||
}
|
||||
do_eqp_test 6.2 {
|
||||
SELECT * FROM e1 WHERE (a, b) < (?, ?)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)<(?,?))}
|
||||
}
|
||||
do_eqp_test 6.3 {
|
||||
SELECT * FROM e1 WHERE c = ? AND (d, e) > (?, ?)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?))}
|
||||
}
|
||||
do_eqp_test 6.4 {
|
||||
SELECT * FROM e1 WHERE c = ? AND (d, e) < (?, ?)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)<(?,?))}
|
||||
}
|
||||
|
||||
do_eqp_test 6.5 {
|
||||
SELECT * FROM e1 WHERE (d, e) BETWEEN (?, ?) AND (?, ?) AND c = ?
|
||||
} {
|
||||
0 0 0
|
||||
{SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?) AND (d,e)<(?,?))}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE f1(a, b, c);
|
||||
CREATE INDEX f1ab ON f1(a, b);
|
||||
}
|
||||
|
||||
do_catchsql_test 7.2 {
|
||||
SELECT (a COLLATE nocase, b) IN (SELECT a, b FROM f1) FROM f1;
|
||||
} {0 {}}
|
||||
|
||||
do_catchsql_test 7.3 {
|
||||
SELECT (a COLLATE nose, b) IN (SELECT a, b FROM f1) FROM f1;
|
||||
} {1 {no such collation sequence: nose}}
|
||||
|
||||
do_catchsql_test 7.4 {
|
||||
SELECT * FROM f1 WHERE (?, ? COLLATE nose) > (a, b);
|
||||
} {1 {no such collation sequence: nose}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
drop_all_tables
|
||||
do_execsql_test 8.1 {
|
||||
CREATE TABLE c1(x, y);
|
||||
CREATE TABLE c2(a, b, c);
|
||||
CREATE INDEX c2ab ON c2(a, b);
|
||||
CREATE INDEX c2c ON c2(c);
|
||||
|
||||
CREATE TABLE c3(d);
|
||||
}
|
||||
do_catchsql_test 8.2 {
|
||||
SELECT * FROM c2 CROSS JOIN c3 WHERE
|
||||
( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR
|
||||
( c == (SELECT x, y FROM c1) AND c3.d = c )
|
||||
} {1 {row value misused}}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,110 @@
|
||||
# 2016 July 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 implements regression tests for SQLite library. The
|
||||
# focus of this file is syntax errors involving row-values and
|
||||
# virtual tables.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue5
|
||||
|
||||
proc vtab_command {method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE t1(a, b, c, d, expr)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set COL(0) a
|
||||
set COL(1) b
|
||||
set COL(2) c
|
||||
set COL(3) d
|
||||
set COL(4) expr
|
||||
|
||||
set OP(eq) =
|
||||
set OP(ne) !=
|
||||
set OP(gt) >
|
||||
set OP(le) <=
|
||||
set OP(lt) <
|
||||
set OP(ge) >=
|
||||
set OP(match) MATCH
|
||||
set OP(like) LIKE
|
||||
set OP(glob) GLOB
|
||||
set OP(regexp) REGEXP
|
||||
|
||||
set clist [lindex $args 0]
|
||||
set ret [list]
|
||||
set elist [list]
|
||||
set i 0
|
||||
foreach c $clist {
|
||||
array set C $c
|
||||
if {$C(usable)} {
|
||||
lappend ret omit $i
|
||||
lappend elist "$COL($C(column)) $OP($C(op)) %$i%"
|
||||
}
|
||||
incr i
|
||||
}
|
||||
|
||||
lappend ret idxstr [join $elist " AND "]
|
||||
#puts "xBestIndex: $ret"
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
foreach {idxnum idxstr arglist} $args {}
|
||||
set i 0
|
||||
set ee $idxstr
|
||||
foreach a $arglist {
|
||||
if {[string is double $a]==0} {
|
||||
set a "'[string map {' ''} $a]'"
|
||||
}
|
||||
set ee [string map [list "%$i%" $a] $ee]
|
||||
incr i
|
||||
}
|
||||
set ee [string map [list "'" "''"] $ee]
|
||||
|
||||
set ret [list sql "SELECT 1, 'a', 'b', 'c', 'd', '$ee'"]
|
||||
#puts "xFilter: $ret"
|
||||
return $ret
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
register_tcl_module db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
|
||||
} {}
|
||||
|
||||
|
||||
foreach {tn where res} {
|
||||
1 "1" {{}}
|
||||
2 "a=1" {{a = 1}}
|
||||
3 "a=1 AND 4 = b" {{a = 1 AND b = 4}}
|
||||
4 "c>'hello'" {{c > 'hello'}}
|
||||
5 "c<='hel''lo'" {{c <= 'hel''lo'}}
|
||||
6 "(a, b) = (SELECT 9, 10)" {{a = 9 AND b = 10}}
|
||||
7 "(+a, b) = (SELECT 'a', 'b')" {{b = 'b'}}
|
||||
8 "(a, +b) = (SELECT 'a', 'b')" {{a = 'a'}}
|
||||
11 "(+a, b) IN (SELECT 'a', 'b')" {{b = 'b'}}
|
||||
12 "(a, +b) IN (SELECT 'a', 'b')" {{a = 'a'}}
|
||||
|
||||
13 "(a, b) < ('d', 'e')" {{a <= 'd'}}
|
||||
14 "(a, b) < ('a', 'c')" {{a <= 'a'}}
|
||||
15 "(a, b) <= ('a', 'b')" {{a <= 'a'}}
|
||||
16 "(a, b) < ('a', 'b')" {}
|
||||
} {
|
||||
do_execsql_test 1.$tn "SELECT expr FROM x1 WHERE $where" $res
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,36 @@
|
||||
# 2016-08-18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# The focus of this file is handling of NULL values in row-value IN
|
||||
# expressions.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue6
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE INDEX t1x1 ON t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,NULL,200);
|
||||
|
||||
CREATE TABLE t2(x,y,z);
|
||||
INSERT INTO t2 VALUES(1,NULL,55);
|
||||
|
||||
SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55);
|
||||
} {}
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1 VALUES(2,3,400);
|
||||
INSERT INTO t2 VALUES(2,3,55);
|
||||
|
||||
SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55);
|
||||
} {400}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,58 @@
|
||||
# 2016-08-18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# The focus of this file is vector assignments in the SET clause of
|
||||
# an UPDATE statement.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue7
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE INDEX t1x ON t1(a,b);
|
||||
INSERT INTO t1(a,b,c,d) VALUES(1,2,0,0),(3,4,0,0),(5,6,0,0);
|
||||
CREATE TABLE t2(w,x,y,z);
|
||||
CREATE INDEX t2x ON t2(w,x);
|
||||
INSERT INTO t2(w,x,y,z) VALUES(1,2,11,22),(8,9,88,99),(3,5,33,55),(5,6,55,66);
|
||||
|
||||
SELECT *,'|' FROM t1 ORDER BY a;
|
||||
} {1 2 0 0 | 3 4 0 0 | 5 6 0 0 |}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE (w,x)=(a,b));
|
||||
SELECT *,'|' FROM t1 ORDER BY a;
|
||||
} {1 2 11 22 | 3 4 {} {} | 5 6 55 66 |}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE w=a);
|
||||
SELECT *,'|' FROM t1 ORDER BY a;
|
||||
} {1 2 11 22 | 3 4 33 55 | 5 6 55 66 |}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
UPDATE t1 SET (c) = 99 WHERE a=3;
|
||||
SELECT *,'|' FROM t1 ORDER BY a;
|
||||
} {1 2 11 22 | 3 4 99 55 | 5 6 55 66 |}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
UPDATE t1 SET b = 8, (c,d) = (SELECT 123,456) WHERE a=3;
|
||||
SELECT *,'|' FROM t1 ORDER BY a;
|
||||
} {1 2 11 22 | 3 8 123 456 | 5 6 55 66 |}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
UPDATE t1 SET (c,d) = (SELECT x,y,z FROM t2 WHERE w=a);
|
||||
} {1 {2 columns assigned 3 values}}
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
UPDATE t1 SET (b,c,d) = (SELECT x,y FROM t2 WHERE w=a);
|
||||
} {1 {3 columns assigned 2 values}}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,59 @@
|
||||
# 2016-08-22
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Use of row values in CASE statements.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue8
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d);
|
||||
INSERT INTO t1(a,b,c,d) VALUES
|
||||
(1,1,2,3),
|
||||
(2,2,3,4),
|
||||
(3,1,2,4),
|
||||
(4,2,3,5),
|
||||
(5,3,4,6),
|
||||
(6,4,5,9);
|
||||
SELECT a, CASE (b,c) WHEN (1,2) THEN 'aleph'
|
||||
WHEN (2,3) THEN 'bet'
|
||||
WHEN (3,4) THEN 'gimel'
|
||||
ELSE '-' END,
|
||||
'|'
|
||||
FROM t1
|
||||
ORDER BY a;
|
||||
} {1 aleph | 2 bet | 3 aleph | 4 bet | 5 gimel | 6 - |}
|
||||
do_execsql_test 1.2 {
|
||||
SELECT a, CASE (b,c,d) WHEN (1,2,3) THEN 'aleph'
|
||||
WHEN (2,3,4) THEN 'bet'
|
||||
WHEN (3,4,6) THEN 'gimel'
|
||||
ELSE '-' END,
|
||||
'|'
|
||||
FROM t1
|
||||
ORDER BY a;
|
||||
} {1 aleph | 2 bet | 3 - | 4 - | 5 gimel | 6 - |}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t2(x,y) VALUES(1,6),(2,5),(3,4),(4,3),(5,2),(6,1);
|
||||
SELECT x, CASE (SELECT b,c FROM t1 WHERE a=y)
|
||||
WHEN (1,2) THEN 'aleph'
|
||||
WHEN (2,3) THEN 'bet'
|
||||
WHEN (3,4) THEN 'gimel'
|
||||
ELSE '-' END,
|
||||
'|'
|
||||
FROM t2
|
||||
ORDER BY +x;
|
||||
} {1 - | 2 gimel | 3 bet | 4 aleph | 5 bet | 6 aleph |}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,302 @@
|
||||
# 2016 September 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing SQL statements that use row value
|
||||
# constructors.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rowvalue9
|
||||
|
||||
# Tests:
|
||||
#
|
||||
# 1.*: Test that affinities are handled correctly by various row-value
|
||||
# operations without indexes.
|
||||
#
|
||||
# 2.*: Test an affinity bug that came up during testing.
|
||||
#
|
||||
# 3.*: Test a row-value version of the bug tested by 2.*.
|
||||
#
|
||||
# 4.*: Test that affinities are handled correctly by various row-value
|
||||
# operations with assorted indexes.
|
||||
#
|
||||
|
||||
do_execsql_test 1.0.1 {
|
||||
CREATE TABLE a1(c, b INTEGER, a TEXT, PRIMARY KEY(a, b));
|
||||
|
||||
INSERT INTO a1 (rowid, c, b, a) VALUES(3, '0x03', 1, 1);
|
||||
INSERT INTO a1 (rowid, c, b, a) VALUES(14, '0x0E', 2, 2);
|
||||
INSERT INTO a1 (rowid, c, b, a) VALUES(15, '0x0F', 3, 3);
|
||||
INSERT INTO a1 (rowid, c, b, a) VALUES(92, '0x5C', 4, 4);
|
||||
|
||||
CREATE TABLE a2(x BLOB, y BLOB);
|
||||
INSERT INTO a2(x, y) VALUES(1, 1);
|
||||
INSERT INTO a2(x, y) VALUES(2, '2');
|
||||
INSERT INTO a2(x, y) VALUES('3', 3);
|
||||
INSERT INTO a2(x, y) VALUES('4', '4');
|
||||
}
|
||||
|
||||
do_execsql_test 1.0.2 {
|
||||
SELECT x, typeof(x), y, typeof(y) FROM a2 ORDER BY rowid
|
||||
} {
|
||||
1 integer 1 integer
|
||||
2 integer 2 text
|
||||
3 text 3 integer
|
||||
4 text 4 text
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
SELECT (SELECT rowid FROM a1 WHERE a=x AND b=y) FROM a2
|
||||
} {{} {} 15 92}
|
||||
do_execsql_test 1.1.2 {
|
||||
SELECT (SELECT rowid FROM a1 WHERE (a, b) = (x, y)) FROM a2
|
||||
} {{} {} 15 92}
|
||||
|
||||
do_execsql_test 1.2.3 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE a=x AND b=y;
|
||||
} {15 92}
|
||||
do_execsql_test 1.2.4 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE (a, b) = (x, y)
|
||||
} {15 92}
|
||||
|
||||
|
||||
do_execsql_test 1.3.1 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE coalesce(NULL,x)=a AND coalesce(NULL,y)=b
|
||||
} {3 14 15 92}
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT a1.rowid FROM a1, a2
|
||||
WHERE (coalesce(NULL,x), coalesce(NULL,y)) = (a, b)
|
||||
} {3 14 15 92}
|
||||
|
||||
do_execsql_test 1.4.1 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE +x=a AND +y=b
|
||||
} {3 14 15 92}
|
||||
do_execsql_test 1.4.2 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE (+x, +y) = (a, b)
|
||||
} {3 14 15 92}
|
||||
|
||||
do_execsql_test 1.5.1 {
|
||||
SELECT (SELECT rowid FROM a1 WHERE a=+x AND b=+y) FROM a2
|
||||
} {3 14 15 92}
|
||||
do_execsql_test 1.5.2 {
|
||||
SELECT (SELECT rowid FROM a1 WHERE (a, b) = (+x, +y)) FROM a2
|
||||
} {3 14 15 92}
|
||||
do_execsql_test 1.5.3 {
|
||||
SELECT (SELECT rowid FROM a1 WHERE (+x, +y) = (a, b)) FROM a2
|
||||
} {3 14 15 92}
|
||||
|
||||
do_execsql_test 1.6.1 {
|
||||
SELECT a1.rowid FROM a1 WHERE (a, b) IN (SELECT x, y FROM a2)
|
||||
} {15 92}
|
||||
do_execsql_test 1.6.2 {
|
||||
SELECT a1.rowid FROM a1, a2 WHERE EXISTS (
|
||||
SELECT 1 FROM a1 WHERE a=x AND b=y
|
||||
)
|
||||
} {3 14 15 92 3 14 15 92}
|
||||
|
||||
# Test that [199df416] is fixed.
|
||||
#
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE b1(a TEXT);
|
||||
CREATE TABLE b2(x BLOB);
|
||||
INSERT INTO b1 VALUES(1);
|
||||
INSERT INTO b2 VALUES(1);
|
||||
}
|
||||
do_execsql_test 2.2 { SELECT * FROM b1, b2 WHERE a=x; } {}
|
||||
do_execsql_test 2.3 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {}
|
||||
do_execsql_test 2.4 { CREATE UNIQUE INDEX b1a ON b1(a); }
|
||||
do_execsql_test 2.5 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {}
|
||||
|
||||
# Test that a multi-column version of the query that revealed problem
|
||||
# [199df416] also works.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE c1(a INTEGER, b TEXT);
|
||||
INSERT INTO c1 VALUES(1, 1);
|
||||
CREATE TABLE c2(x BLOB, y BLOB);
|
||||
INSERT INTO c2 VALUES(1, 1);
|
||||
}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2)
|
||||
} {}
|
||||
do_execsql_test 3.3 {
|
||||
CREATE UNIQUE INDEX c1ab ON c1(a, b);
|
||||
SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2)
|
||||
} {}
|
||||
do_execsql_test 3.4 {
|
||||
SELECT * FROM c1 WHERE (a, +b) IN (SELECT x, y FROM c2)
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.5 {
|
||||
SELECT c1.rowid FROM c1 WHERE b = (SELECT y FROM c2);
|
||||
} {}
|
||||
do_execsql_test 3.6 {
|
||||
SELECT c1.rowid FROM c1 WHERE (a, b) = (SELECT x, y FROM c2);
|
||||
} {}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE d1(a TEXT, b INTEGER, c NUMERIC);
|
||||
CREATE TABLE d2(x BLOB, y BLOB);
|
||||
|
||||
INSERT INTO d1 VALUES(1, 1, 1);
|
||||
INSERT INTO d1 VALUES(2, 2, 2);
|
||||
INSERT INTO d1 VALUES(3, 3, 3);
|
||||
INSERT INTO d1 VALUES(4, 4, 4);
|
||||
|
||||
INSERT INTO d2 VALUES (1, 1);
|
||||
INSERT INTO d2 VALUES (2, '2');
|
||||
INSERT INTO d2 VALUES ('3', 3);
|
||||
INSERT INTO d2 VALUES ('4', '4');
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
1 {}
|
||||
2 { CREATE INDEX idx ON d1(a) }
|
||||
3 { CREATE INDEX idx ON d1(a, c) }
|
||||
4 { CREATE INDEX idx ON d1(c) }
|
||||
5 { CREATE INDEX idx ON d1(c, a) }
|
||||
|
||||
6 {
|
||||
CREATE INDEX idx ON d1(c, a) ;
|
||||
CREATE INDEX idx1 ON d2(x, y);
|
||||
}
|
||||
|
||||
7 {
|
||||
CREATE INDEX idx ON d1(c, a) ;
|
||||
CREATE UNIQUE INDEX idx2 ON d2(x, y) ;
|
||||
}
|
||||
|
||||
8 {
|
||||
CREATE INDEX idx ON d1(c) ;
|
||||
CREATE UNIQUE INDEX idx2 ON d2(x);
|
||||
}
|
||||
|
||||
} {
|
||||
execsql { DROP INDEX IF EXISTS idx }
|
||||
execsql { DROP INDEX IF EXISTS idx2 }
|
||||
execsql { DROP INDEX IF EXISTS idx3 }
|
||||
execsql $idx
|
||||
|
||||
do_execsql_test 4.$tn.1 {
|
||||
SELECT rowid FROM d1 WHERE (a, c) IN (SELECT x, y FROM d2);
|
||||
} {3 4}
|
||||
|
||||
do_execsql_test 4.$tn.2 {
|
||||
SELECT rowid FROM d1 WHERE (c, a) IN (SELECT x, y FROM d2);
|
||||
} {2 4}
|
||||
|
||||
do_execsql_test 4.$tn.3 {
|
||||
SELECT rowid FROM d1 WHERE (+c, a) IN (SELECT x, y FROM d2);
|
||||
} {2}
|
||||
|
||||
do_execsql_test 4.$tn.4 {
|
||||
SELECT rowid FROM d1 WHERE (c, a) = (
|
||||
SELECT x, y FROM d2 WHERE d2.rowid=d1.rowid
|
||||
);
|
||||
} {2 4}
|
||||
|
||||
do_execsql_test 4.$tn.5 {
|
||||
SELECT d1.rowid FROM d1, d2 WHERE a = y;
|
||||
} {2 4}
|
||||
|
||||
do_execsql_test 4.$tn.6 {
|
||||
SELECT d1.rowid FROM d1 WHERE a = (
|
||||
SELECT y FROM d2 where d2.rowid=d1.rowid
|
||||
);
|
||||
} {2 4}
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE e1(a TEXT, c NUMERIC);
|
||||
CREATE TABLE e2(x BLOB, y BLOB);
|
||||
|
||||
INSERT INTO e1 VALUES(2, 2);
|
||||
|
||||
INSERT INTO e2 VALUES ('2', 2);
|
||||
INSERT INTO e2 VALUES ('2', '2');
|
||||
INSERT INTO e2 VALUES ('2', '2.0');
|
||||
|
||||
CREATE INDEX e1c ON e1(c);
|
||||
}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
SELECT rowid FROM e1 WHERE (a, c) IN (SELECT x, y FROM e2);
|
||||
} {1}
|
||||
do_execsql_test 5.2 {
|
||||
SELECT rowid FROM e2 WHERE rowid IN (SELECT +c FROM e1);
|
||||
} {2}
|
||||
do_execsql_test 5.3 {
|
||||
SELECT rowid FROM e2 WHERE rowid IN (SELECT 0+c FROM e1);
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE f1(a, b);
|
||||
CREATE TABLE f2(c, d);
|
||||
CREATE TABLE f3(e, f);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
SELECT * FROM f3 WHERE (e, f) IN (
|
||||
SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2
|
||||
);
|
||||
}
|
||||
do_execsql_test 6.2 {
|
||||
CREATE INDEX f3e ON f3(e);
|
||||
SELECT * FROM f3 WHERE (e, f) IN (
|
||||
SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE g1(a, b);
|
||||
INSERT INTO g1 VALUES
|
||||
(1, 1), (1, 2), (1, 3), (1, 'i'), (1, 'j'),
|
||||
(1, 6), (1, 7), (1, 8), (1, 9), (1, 10),
|
||||
(1, 4), (1, 5);
|
||||
|
||||
CREATE TABLE g2(x, y);
|
||||
CREATE INDEX g2x ON g2(x);
|
||||
|
||||
INSERT INTO g2 VALUES(1, 4);
|
||||
INSERT INTO g2 VALUES(1, 5);
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT * FROM g2 WHERE (x, y) IN (
|
||||
SELECT a, b FROM g1 ORDER BY +a, +b LIMIT 10
|
||||
);
|
||||
} { 1 4 1 5 }
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
SELECT * FROM g2 WHERE (x, y) IN (
|
||||
SELECT a, b FROM g1 ORDER BY a, b LIMIT 10
|
||||
);
|
||||
} { 1 4 1 5 }
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
SELECT * FROM g2 WHERE (x, y) IN (
|
||||
SELECT a, b FROM g1 ORDER BY 1, 2 LIMIT 10
|
||||
);
|
||||
} { 1 4 1 5 }
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix rowvaluefault
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE xyz(one, two, thr, fou);
|
||||
INSERT INTO xyz VALUES('A', 'A', 'A', 1);
|
||||
INSERT INTO xyz VALUES('B', 'B', 'B', 2);
|
||||
INSERT INTO xyz VALUES('C', 'C', 'C', 3);
|
||||
INSERT INTO xyz VALUES('D', 'D', 'D', 4);
|
||||
|
||||
CREATE UNIQUE INDEX xyz_one_two ON xyz(one, two);
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom* -body {
|
||||
execsql { SELECT fou FROM xyz WHERE (one, two, thr) = ('B', 'B', 'B') }
|
||||
} -test {
|
||||
faultsim_test_result {0 2}
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -faults oom* -body {
|
||||
execsql { SELECT fou FROM xyz WHERE (two, thr) IS ('C', 'C') }
|
||||
} -test {
|
||||
faultsim_test_result {0 3}
|
||||
}
|
||||
|
||||
do_faultsim_test 3 -faults oom* -body {
|
||||
execsql { SELECT fou FROM xyz WHERE (one, two, thr) > ('B', 'B', 'B') }
|
||||
} -test {
|
||||
faultsim_test_result {0 {3 4}}
|
||||
}
|
||||
|
||||
do_faultsim_test 4 -faults oom* -body {
|
||||
execsql { SELECT fou FROM xyz WHERE (one, two) IN (SELECT one, two FROM xyz) }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
|
||||
do_faultsim_test 5 -faults oom* -body {
|
||||
execsql {
|
||||
SELECT fou FROM xyz
|
||||
WHERE (one, two, thr) IN (SELECT one, two, thr FROM xyz)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
|
||||
do_faultsim_test 6 -faults oom* -body {
|
||||
execsql {
|
||||
SELECT fou FROM xyz
|
||||
WHERE (one, two, thr) BETWEEN ('B', 'B', 'B') AND ('C', 'C', 'C') }
|
||||
} -test {
|
||||
faultsim_test_result {0 {2 3}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+4
-8
@@ -114,26 +114,22 @@ ifcapable {subquery && compound} {
|
||||
CREATE TABLE t2(a,b);
|
||||
SELECT 5 IN (SELECT a,b FROM t2);
|
||||
}
|
||||
} [list 1 \
|
||||
{only a single result allowed for a SELECT that is part of an expression}]
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test select7-5.2 {
|
||||
catchsql {
|
||||
SELECT 5 IN (SELECT * FROM t2);
|
||||
}
|
||||
} [list 1 \
|
||||
{only a single result allowed for a SELECT that is part of an expression}]
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test select7-5.3 {
|
||||
catchsql {
|
||||
SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
|
||||
}
|
||||
} [list 1 \
|
||||
{only a single result allowed for a SELECT that is part of an expression}]
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
do_test select7-5.4 {
|
||||
catchsql {
|
||||
SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
|
||||
}
|
||||
} [list 1 \
|
||||
{only a single result allowed for a SELECT that is part of an expression}]
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
}
|
||||
|
||||
# Verify that an error occurs if you have too many terms on a
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#
|
||||
# shell4-1.*: Basic tests specific to the "stats" command.
|
||||
# shell4-2.*: Basic tests for ".trace"
|
||||
# shell4-3.*: The ".read" command takes the shell out of interactive mode
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -124,5 +125,17 @@ do_test shell4-2.5 {
|
||||
} {0 {SELECT * FROM t1;}}
|
||||
}
|
||||
|
||||
do_test shell4-3.1 {
|
||||
set fd [open t1.txt wb]
|
||||
puts $fd "SELECT 'squirrel';"
|
||||
close $fd
|
||||
exec $::CLI :memory: --interactive ".read t1.txt"
|
||||
} {squirrel}
|
||||
do_test shell4-3.2 {
|
||||
set fd [open t1.txt wb]
|
||||
puts $fd "SELECT 'pound: \302\243';"
|
||||
close $fd
|
||||
exec $::CLI :memory: --interactive ".read t1.txt"
|
||||
} {pound: £}
|
||||
|
||||
finish_test
|
||||
|
||||
+4
-4
@@ -105,7 +105,7 @@ proc io_log {db} {
|
||||
puts "Normal : Read $stats2(read), wrote $stats2(write)"
|
||||
}
|
||||
|
||||
proc reset_db {} {
|
||||
proc speed3_reset_db {} {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
@@ -117,7 +117,7 @@ proc reset_db {} {
|
||||
}
|
||||
|
||||
forcedelete test2.db test2.db-journal
|
||||
reset_db
|
||||
speed3_reset_db
|
||||
|
||||
# Set up a database in auto-vacuum mode and create a database schema.
|
||||
#
|
||||
@@ -154,7 +154,7 @@ do_test speed3-0.4 {
|
||||
# Delete all content in a table, one row at a time.
|
||||
#
|
||||
#io_log db
|
||||
reset_db
|
||||
speed3_reset_db
|
||||
speed_trial speed3-1.incrvacuum $::NROW row {DELETE FROM main.t1 WHERE 1}
|
||||
speed_trial speed3-1.normal $::NROW row {DELETE FROM aux.t1 WHERE 1}
|
||||
io_log db
|
||||
@@ -164,7 +164,7 @@ io_log db
|
||||
#
|
||||
#db eval {PRAGMA incremental_vacuum(500000)}
|
||||
populate_t1 db
|
||||
reset_db
|
||||
speed3_reset_db
|
||||
speed_trial speed3-2.incrvacuum $::NROW row {SELECT c FROM main.t1}
|
||||
speed_trial speed3-2.normal $::NROW row {SELECT c FROM aux.t1}
|
||||
io_log db
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ do_test subselect-1.1 {
|
||||
do_test subselect-1.2 {
|
||||
set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
|
||||
lappend v $msg
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
} {1 {row value misused}}
|
||||
|
||||
# A subselect without an aggregate.
|
||||
#
|
||||
|
||||
+26
-3
@@ -25,6 +25,7 @@
|
||||
# copy_file FROM TO
|
||||
# delete_file FILENAME
|
||||
# drop_all_tables ?DB?
|
||||
# drop_all_indexes ?DB?
|
||||
# forcecopy FROM TO
|
||||
# forcedelete FILENAME
|
||||
#
|
||||
@@ -1289,9 +1290,9 @@ proc explain_i {sql {db db}} {
|
||||
set D ""
|
||||
}
|
||||
foreach opcode {
|
||||
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
|
||||
Seek SeekGE SeekGT SeekLE SeekLT NotFound Last Rewind
|
||||
NoConflict Next Prev VNext VPrev VFilter
|
||||
SorterSort SorterNext
|
||||
SorterSort SorterNext NextIfOpen
|
||||
} {
|
||||
set color($opcode) $B
|
||||
}
|
||||
@@ -1312,9 +1313,15 @@ proc explain_i {sql {db db}} {
|
||||
set bSeenGoto 1
|
||||
}
|
||||
|
||||
if {$opcode=="Once"} {
|
||||
for {set i $addr} {$i<$p2} {incr i} {
|
||||
set star($i) $addr
|
||||
}
|
||||
}
|
||||
|
||||
if {$opcode=="Next" || $opcode=="Prev"
|
||||
|| $opcode=="VNext" || $opcode=="VPrev"
|
||||
|| $opcode=="SorterNext"
|
||||
|| $opcode=="SorterNext" || $opcode=="NextIfOpen"
|
||||
} {
|
||||
for {set i $p2} {$i<$addr} {incr i} {
|
||||
incr x($i) 2
|
||||
@@ -1338,6 +1345,12 @@ proc explain_i {sql {db db}} {
|
||||
}
|
||||
set I [string repeat " " $x($addr)]
|
||||
|
||||
if {[info exists star($addr)]} {
|
||||
set ii [expr $x($star($addr))]
|
||||
append I " "
|
||||
set I [string replace $I $ii $ii *]
|
||||
}
|
||||
|
||||
set col ""
|
||||
catch { set col $color($opcode) }
|
||||
|
||||
@@ -1939,6 +1952,16 @@ proc drop_all_tables {{db db}} {
|
||||
}
|
||||
}
|
||||
|
||||
# Drop all auxiliary indexes from the main database opened by handle [db].
|
||||
#
|
||||
proc drop_all_indexes {{db db}} {
|
||||
set L [$db eval {
|
||||
SELECT name FROM sqlite_master WHERE type='index' AND sql LIKE 'create%'
|
||||
}]
|
||||
foreach idx $L { $db eval "DROP INDEX $idx" }
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# If a test script is executed with global variable $::G(perm:name) set to
|
||||
# "wal", then the tests are run in WAL mode. Otherwise, they should be run
|
||||
|
||||
+1
-1
@@ -333,7 +333,7 @@ ifcapable subquery {
|
||||
test_boolset types2-8.8 {o IN (SELECT t FROM t4)} {7}
|
||||
test_boolset types2-8.9 {i IN (SELECT o FROM t4)} {9 10 11 12}
|
||||
test_boolset types2-8.6 {n IN (SELECT o FROM t4)} {9 10 11 12}
|
||||
test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {9 11}
|
||||
test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {}
|
||||
test_boolset types2-8.8 {o IN (SELECT o FROM t4)} {9 10}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# 2016-08-19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements a test for VACUUM on attached databases.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If the VACUUM statement is disabled in the current build, skip all
|
||||
# the tests in this file.
|
||||
#
|
||||
ifcapable !vacuum {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
forcedelete test2.db test3.db
|
||||
do_execsql_test vacuum5-1.1 {
|
||||
CREATE TABLE main.t1(a,b);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
|
||||
INSERT INTO t1(a,b) SELECT x, randomblob(1000) FROM c;
|
||||
CREATE TEMP TABLE ttemp(x,y);
|
||||
INSERT INTO ttemp SELECT * FROM t1;
|
||||
ATTACH 'test2.db' AS x2;
|
||||
ATTACH 'test3.db' AS x3;
|
||||
CREATE TABLE x2.t2(c,d);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
CREATE TABLE x3.t3(e,f);
|
||||
INSERT INTO t3 SELECT * FROM t1;
|
||||
DELETE FROM t1 WHERE (rowid%3)!=0;
|
||||
DELETE FROM t2 WHERE (rowid%4)!=0;
|
||||
DELETE FROM t3 WHERE (rowid%5)!=0;
|
||||
PRAGMA main.integrity_check;
|
||||
PRAGMA x2.integrity_check;
|
||||
PRAGMA x3.integrity_check;
|
||||
} {ok ok ok}
|
||||
set size1 [file size test.db]
|
||||
set size2 [file size test2.db]
|
||||
set size3 [file size test3.db]
|
||||
|
||||
do_execsql_test vacuum5-1.2.1 {
|
||||
VACUUM main;
|
||||
} {}
|
||||
do_test vacuum5-1.2.2 {
|
||||
expr {[file size test.db]<$size1}
|
||||
} {1}
|
||||
do_test vacuum5-1.2.3 {
|
||||
file size test2.db
|
||||
} $size2
|
||||
do_test vacuum5-1.2.4 {
|
||||
file size test3.db
|
||||
} $size3
|
||||
set size1 [file size test.db]
|
||||
do_execsql_test vacuum-1.2.5 {
|
||||
DELETE FROM t1;
|
||||
PRAGMA main.integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_execsql_test vacuum5-1.3.1 {
|
||||
VACUUM x2;
|
||||
} {}
|
||||
do_test vacuum5-1.3.2 {
|
||||
file size test.db
|
||||
} $size1
|
||||
do_test vacuum5-1.3.3 {
|
||||
expr {[file size test2.db]<$size2}
|
||||
} 1
|
||||
do_test vacuum5-1.3.4 {
|
||||
file size test3.db
|
||||
} $size3
|
||||
set size2 [file size test2.db]
|
||||
do_execsql_test vacuum-1.3.5 {
|
||||
DELETE FROM t2;
|
||||
PRAGMA x2.integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_execsql_test vacuum5-1.4.1 {
|
||||
VACUUM x3;
|
||||
} {}
|
||||
do_test vacuum5-1.3.2 {
|
||||
file size test.db
|
||||
} $size1
|
||||
do_test vacuum5-1.3.3 {
|
||||
file size test2.db
|
||||
} $size2
|
||||
do_test vacuum5-1.3.4 {
|
||||
expr {[file size test3.db]<$size3}
|
||||
} 1
|
||||
|
||||
# VACUUM is a no-op on the TEMP table
|
||||
#
|
||||
set sizeTemp [db one {PRAGMA temp.page_count}]
|
||||
do_execsql_test vacuum5-1.4.1 {
|
||||
VACUUM temp;
|
||||
} {}
|
||||
do_execsql_test vacuum5-1.4.2 {
|
||||
PRAGMA temp.page_count;
|
||||
} $sizeTemp
|
||||
|
||||
do_catchsql_test vacuum5-2.0 {
|
||||
VACUUM olaf;
|
||||
} {1 {unknown database olaf}}
|
||||
+2
-2
@@ -21,7 +21,7 @@ ifcapable !vtab||!schema_pragmas { finish_test ; return }
|
||||
# Register the echo module
|
||||
register_echo_module [sqlite3_connection_pointer db]
|
||||
|
||||
do_test vtabE-1.1 {
|
||||
do_test vtabF-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
@@ -37,7 +37,7 @@ do_test vtabE-1.1 {
|
||||
SELECT b FROM t1 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {110 111 112 113}
|
||||
do_test vtabE-1.2 {
|
||||
do_test vtabF-1.2 {
|
||||
execsql {SELECT b FROM tv1 WHERE a IS NOT NULL}
|
||||
} {110 111 112 113}
|
||||
|
||||
|
||||
+26
-2
@@ -108,7 +108,12 @@ foreach ::tclvar_set_omit {0 1} {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
if {1} {
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
set drive [string range [pwd] 0 1]
|
||||
set ::env(fstreeDrive) $drive
|
||||
}
|
||||
if {$tcl_platform(platform)!="windows" || \
|
||||
[regexp -nocase -- {^[A-Z]:} $drive]} {
|
||||
reset_db
|
||||
register_fs_module db
|
||||
do_execsql_test 3.0 {
|
||||
@@ -119,7 +124,7 @@ if {1} {
|
||||
proc list_root_files {} {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
set res [list]
|
||||
foreach name [glob -directory $::env(SystemDrive)/ -- *] {
|
||||
foreach name [glob -directory $::env(fstreeDrive)/ -- *] {
|
||||
if {[string index [file tail $name] 0] eq "."} continue
|
||||
lappend res $name
|
||||
}
|
||||
@@ -142,6 +147,25 @@ if {1} {
|
||||
}
|
||||
}
|
||||
|
||||
# Read the first 5 entries from the root directory. Except, ignore
|
||||
# files that contain the "$" character in their names as these are
|
||||
# special files on some Windows platforms.
|
||||
#
|
||||
set res [list]
|
||||
set root_files [list_root_files]
|
||||
set num_root_files [llength $root_files]
|
||||
set lim_root_files [expr {$num_root_files > 5 ? 5 : $num_root_files}]
|
||||
foreach p [lrange $root_files 0 [expr {$lim_root_files - 1}]] {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
if {[regexp {\$} $p]} {incr lim_root_files -1} else {lappend res $p}
|
||||
} else {
|
||||
lappend res "/$p"
|
||||
}
|
||||
}
|
||||
do_execsql_test 3.1 [subst {
|
||||
SELECT path FROM fstree WHERE path NOT GLOB '*\$*' LIMIT $lim_root_files;
|
||||
}] $res
|
||||
|
||||
# Read all entries in the current directory.
|
||||
#
|
||||
proc contents {pattern} {
|
||||
|
||||
@@ -37,6 +37,8 @@ set extras {
|
||||
UMINUS
|
||||
UPLUS
|
||||
REGISTER
|
||||
VECTOR
|
||||
SELECT_COLUMN
|
||||
ASTERISK
|
||||
SPAN
|
||||
SPACE
|
||||
|
||||
@@ -312,6 +312,326 @@ static void sqlEvalFunc(
|
||||
/* End of the eval() implementation
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
** The generate_series(START,END,STEP) eponymous table-valued function.
|
||||
**
|
||||
** This code is copy/pasted from ext/misc/series.c in the SQLite source tree.
|
||||
*/
|
||||
/* series_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 series_cursor series_cursor;
|
||||
struct series_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
int isDesc; /* True to count down rather than up */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
sqlite3_int64 iValue; /* Current value ("value") */
|
||||
sqlite3_int64 mnValue; /* Mimimum value ("start") */
|
||||
sqlite3_int64 mxValue; /* Maximum value ("stop") */
|
||||
sqlite3_int64 iStep; /* Increment ("step") */
|
||||
};
|
||||
|
||||
/*
|
||||
** The seriesConnect() method is invoked to create a new
|
||||
** series_vtab that describes the generate_series virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for series_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the series_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against generate_series will look like.
|
||||
*/
|
||||
static int seriesConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
/* Column numbers */
|
||||
#define SERIES_COLUMN_VALUE 0
|
||||
#define SERIES_COLUMN_START 1
|
||||
#define SERIES_COLUMN_STOP 2
|
||||
#define SERIES_COLUMN_STEP 3
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step 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 series_cursor objects.
|
||||
*/
|
||||
static int seriesDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new series_cursor object.
|
||||
*/
|
||||
static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
series_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 series_cursor.
|
||||
*/
|
||||
static int seriesClose(sqlite3_vtab_cursor *cur){
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a series_cursor to its next row of output.
|
||||
*/
|
||||
static int seriesNext(sqlite3_vtab_cursor *cur){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
if( pCur->isDesc ){
|
||||
pCur->iValue -= pCur->iStep;
|
||||
}else{
|
||||
pCur->iValue += pCur->iStep;
|
||||
}
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the series_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int seriesColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
sqlite3_int64 x = 0;
|
||||
switch( i ){
|
||||
case SERIES_COLUMN_START: x = pCur->mnValue; break;
|
||||
case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
|
||||
case SERIES_COLUMN_STEP: x = pCur->iStep; break;
|
||||
default: x = pCur->iValue; break;
|
||||
}
|
||||
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 seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
series_cursor *pCur = (series_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 seriesEof(sqlite3_vtab_cursor *cur){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
if( pCur->isDesc ){
|
||||
return pCur->iValue < pCur->mnValue;
|
||||
}else{
|
||||
return pCur->iValue > pCur->mxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* True to cause run-time checking of the start=, stop=, and/or step=
|
||||
** parameters. The only reason to do this is for testing the
|
||||
** constraint checking logic for virtual tables in the SQLite core.
|
||||
*/
|
||||
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
|
||||
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the series_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to seriesColumn() or seriesRowid() or
|
||||
** seriesEof().
|
||||
**
|
||||
** The query plan selected by seriesBestIndex is passed in the idxNum
|
||||
** parameter. (idxStr is not used in this implementation.) idxNum
|
||||
** is a bitmask showing which constraints are available:
|
||||
**
|
||||
** 1: start=VALUE
|
||||
** 2: stop=VALUE
|
||||
** 4: step=VALUE
|
||||
**
|
||||
** Also, if bit 8 is set, that means that the series should be output
|
||||
** in descending order rather than in ascending order.
|
||||
**
|
||||
** This routine should initialize the cursor and position it so that it
|
||||
** is pointing at the first row, or pointing off the end of the table
|
||||
** (so that seriesEof() will return true) if the table is empty.
|
||||
*/
|
||||
static int seriesFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
series_cursor *pCur = (series_cursor *)pVtabCursor;
|
||||
int i = 0;
|
||||
if( idxNum & 1 ){
|
||||
pCur->mnValue = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->mnValue = 0;
|
||||
}
|
||||
if( idxNum & 2 ){
|
||||
pCur->mxValue = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->mxValue = 0xffffffff;
|
||||
}
|
||||
if( idxNum & 4 ){
|
||||
pCur->iStep = sqlite3_value_int64(argv[i++]);
|
||||
if( pCur->iStep<1 ) pCur->iStep = 1;
|
||||
}else{
|
||||
pCur->iStep = 1;
|
||||
}
|
||||
if( idxNum & 8 ){
|
||||
pCur->isDesc = 1;
|
||||
pCur->iValue = pCur->mxValue;
|
||||
if( pCur->iStep>0 ){
|
||||
pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
|
||||
}
|
||||
}else{
|
||||
pCur->isDesc = 0;
|
||||
pCur->iValue = pCur->mnValue;
|
||||
}
|
||||
pCur->iRowid = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the generate_series 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.
|
||||
**
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** (1) start = $value -- constraint exists
|
||||
** (2) stop = $value -- constraint exists
|
||||
** (4) step = $value -- constraint exists
|
||||
** (8) output in descending order
|
||||
*/
|
||||
static int seriesBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop over constraints */
|
||||
int idxNum = 0; /* The query plan bitmask */
|
||||
int startIdx = -1; /* Index of the start= constraint, or -1 if none */
|
||||
int stopIdx = -1; /* Index of the stop= constraint, or -1 if none */
|
||||
int stepIdx = -1; /* Index of the step= constraint, or -1 if none */
|
||||
int nArg = 0; /* Number of arguments that seriesFilter() expects */
|
||||
|
||||
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 SERIES_COLUMN_START:
|
||||
startIdx = i;
|
||||
idxNum |= 1;
|
||||
break;
|
||||
case SERIES_COLUMN_STOP:
|
||||
stopIdx = i;
|
||||
idxNum |= 2;
|
||||
break;
|
||||
case SERIES_COLUMN_STEP:
|
||||
stepIdx = i;
|
||||
idxNum |= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( startIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[startIdx].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[startIdx].omit= !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
||||
}
|
||||
if( stopIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[stopIdx].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[stopIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
||||
}
|
||||
if( stepIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[stepIdx].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[stepIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
||||
}
|
||||
if( (idxNum & 3)==3 ){
|
||||
/* Both start= and stop= boundaries are available. This is the
|
||||
** the preferred case */
|
||||
pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
|
||||
pIdxInfo->estimatedRows = 1000;
|
||||
if( pIdxInfo->nOrderBy==1 ){
|
||||
if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8;
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}else{
|
||||
/* If either boundary is missing, we have to generate a huge span
|
||||
** of numbers. Make this case very expensive so that the query
|
||||
** planner will work hard to avoid it. */
|
||||
pIdxInfo->estimatedCost = (double)2147483647;
|
||||
pIdxInfo->estimatedRows = 2147483647;
|
||||
}
|
||||
pIdxInfo->idxNum = idxNum;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** generate_series virtual table.
|
||||
*/
|
||||
static sqlite3_module seriesModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
seriesConnect, /* xConnect */
|
||||
seriesBestIndex, /* xBestIndex */
|
||||
seriesDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
seriesOpen, /* xOpen - open a cursor */
|
||||
seriesClose, /* xClose - close a cursor */
|
||||
seriesFilter, /* xFilter - configure scan constraints */
|
||||
seriesNext, /* xNext - advance a cursor */
|
||||
seriesEof, /* xEof - check for end of scan */
|
||||
seriesColumn, /* xColumn - read data */
|
||||
seriesRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
/* END the generate_series(START,END,STEP) implementation
|
||||
*********************************************************************************/
|
||||
|
||||
/*
|
||||
** Print sketchy documentation for this utility program
|
||||
*/
|
||||
@@ -729,6 +1049,7 @@ int main(int argc, char **argv){
|
||||
#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_create_module(db, "generate_series", &seriesModule, 0);
|
||||
sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 1000000);
|
||||
if( zEncoding ) sqlexec(db, "PRAGMA encoding=%s", zEncoding);
|
||||
if( pageSize ) sqlexec(db, "PRAGMA pagesize=%d", pageSize);
|
||||
|
||||
+11
-8
@@ -263,7 +263,8 @@ struct symbol {
|
||||
int useCnt; /* Number of times used */
|
||||
char *destructor; /* Code which executes whenever this symbol is
|
||||
** popped from the stack during error processing */
|
||||
int destLineno; /* Line number for start of destructor */
|
||||
int destLineno; /* Line number for start of destructor. Set to
|
||||
** -1 for duplicate destructors. */
|
||||
char *datatype; /* The data type of information held by this
|
||||
** object. Only used if type==NONTERMINAL */
|
||||
int dtnum; /* The data type number. In the parser, the value
|
||||
@@ -4231,20 +4232,21 @@ void ReportTable(
|
||||
fprintf(out, "};\n"); lineno++;
|
||||
|
||||
/* Output the yy_shift_ofst[] table */
|
||||
fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++;
|
||||
n = lemp->nxstate;
|
||||
while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
|
||||
fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", lemp->nactiontab); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++;
|
||||
fprintf(out, "static const %s yy_shift_ofst[] = {\n",
|
||||
minimum_size_type(mnTknOfst-1, mxTknOfst, &sz)); lineno++;
|
||||
minimum_size_type(mnTknOfst, lemp->nterminal+lemp->nactiontab, &sz));
|
||||
lineno++;
|
||||
lemp->tablesize += n*sz;
|
||||
for(i=j=0; i<n; i++){
|
||||
int ofst;
|
||||
stp = lemp->sorted[i];
|
||||
ofst = stp->iTknOfst;
|
||||
if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1;
|
||||
if( ofst==NO_OFFSET ) ofst = lemp->nactiontab;
|
||||
if( j==0 ) fprintf(out," /* %5d */ ", i);
|
||||
fprintf(out, " %4d,", ofst);
|
||||
if( j==9 || i==n-1 ){
|
||||
@@ -4384,6 +4386,7 @@ void ReportTable(
|
||||
for(i=0; i<lemp->nsymbol; i++){
|
||||
struct symbol *sp = lemp->symbols[i];
|
||||
if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue;
|
||||
if( sp->destLineno<0 ) continue; /* Already emitted */
|
||||
fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++;
|
||||
|
||||
/* Combine duplicate destructors into a single case */
|
||||
@@ -4394,7 +4397,7 @@ void ReportTable(
|
||||
&& strcmp(sp->destructor,sp2->destructor)==0 ){
|
||||
fprintf(out," case %d: /* %s */\n",
|
||||
sp2->index, sp2->name); lineno++;
|
||||
sp2->destructor = 0;
|
||||
sp2->destLineno = -1; /* Avoid emitting this destructor again */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-36
@@ -116,7 +116,7 @@
|
||||
**
|
||||
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||
** and YY_MAX_REDUCE
|
||||
|
||||
**
|
||||
** N == YY_ERROR_ACTION A syntax error has occurred.
|
||||
**
|
||||
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
||||
@@ -125,16 +125,20 @@
|
||||
** slots in the yy_action[] table.
|
||||
**
|
||||
** The action table is constructed as a single large table named yy_action[].
|
||||
** Given state S and lookahead X, the action is computed as
|
||||
** Given state S and lookahead X, the action is computed as either:
|
||||
**
|
||||
** yy_action[ yy_shift_ofst[S] + X ]
|
||||
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
||||
** (B) N = yy_default[S]
|
||||
**
|
||||
** If the index value yy_shift_ofst[S]+X is out of range or if the value
|
||||
** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
|
||||
** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
|
||||
** and that yy_default[S] should be used instead.
|
||||
** The (A) formula is preferred. The B formula is used instead if:
|
||||
** (1) The yy_shift_ofst[S]+X value is out of range, or
|
||||
** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
||||
** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
||||
** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
||||
** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
||||
** Hence only tests (1) and (2) need to be evaluated.)
|
||||
**
|
||||
** The formula above is for computing the action when the lookahead is
|
||||
** The formulas above are for computing the action when the lookahead is
|
||||
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||
@@ -450,50 +454,47 @@ static unsigned int yy_find_shift_action(
|
||||
assert( stateno <= YY_SHIFT_COUNT );
|
||||
do{
|
||||
i = yy_shift_ofst[stateno];
|
||||
if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
|
||||
assert( iLookAhead!=YYNOCODE );
|
||||
i += iLookAhead;
|
||||
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
|
||||
if( iLookAhead>0 ){
|
||||
#ifdef YYFALLBACK
|
||||
YYCODETYPE iFallback; /* Fallback token */
|
||||
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
||||
&& (iFallback = yyFallback[iLookAhead])!=0 ){
|
||||
YYCODETYPE iFallback; /* Fallback token */
|
||||
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
||||
&& (iFallback = yyFallback[iLookAhead])!=0 ){
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
|
||||
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
|
||||
}
|
||||
#endif
|
||||
assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
|
||||
iLookAhead = iFallback;
|
||||
continue;
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
|
||||
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
|
||||
}
|
||||
#endif
|
||||
assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
|
||||
iLookAhead = iFallback;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#ifdef YYWILDCARD
|
||||
{
|
||||
int j = i - iLookAhead + YYWILDCARD;
|
||||
if(
|
||||
{
|
||||
int j = i - iLookAhead + YYWILDCARD;
|
||||
if(
|
||||
#if YY_SHIFT_MIN+YYWILDCARD<0
|
||||
j>=0 &&
|
||||
j>=0 &&
|
||||
#endif
|
||||
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
|
||||
j<YY_ACTTAB_COUNT &&
|
||||
j<YY_ACTTAB_COUNT &&
|
||||
#endif
|
||||
yy_lookahead[j]==YYWILDCARD
|
||||
){
|
||||
yy_lookahead[j]==YYWILDCARD && iLookAhead>0
|
||||
){
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
|
||||
yyTracePrompt, yyTokenName[iLookAhead],
|
||||
yyTokenName[YYWILDCARD]);
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
return yy_action[j];
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
|
||||
yyTracePrompt, yyTokenName[iLookAhead],
|
||||
yyTokenName[YYWILDCARD]);
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
return yy_action[j];
|
||||
}
|
||||
#endif /* YYWILDCARD */
|
||||
}
|
||||
#endif /* YYWILDCARD */
|
||||
return yy_default[stateno];
|
||||
}else{
|
||||
return yy_action[i];
|
||||
|
||||
+8
-6
@@ -1179,8 +1179,9 @@ static void getRbudiffQuery(
|
||||
strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
||||
strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
||||
strPrintf(pSql, " main.%Q AS o WHERE ", zTab);
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
||||
strPrintf(pSql, "\n)");
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
||||
strPrintf(pSql, "\n) AND ");
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
||||
|
||||
/* Deleted rows: */
|
||||
strPrintf(pSql, "\nUNION ALL\nSELECT ");
|
||||
@@ -1194,8 +1195,9 @@ static void getRbudiffQuery(
|
||||
strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
||||
strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
||||
strPrintf(pSql, " aux.%Q AS o WHERE ", zTab);
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
||||
strPrintf(pSql, "\n) ");
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
||||
strPrintf(pSql, "\n) AND ");
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
||||
|
||||
/* Updated rows. If all table columns are part of the primary key, there
|
||||
** can be no updates. In this case this part of the compound SELECT can
|
||||
@@ -1226,7 +1228,7 @@ static void getRbudiffQuery(
|
||||
);
|
||||
|
||||
strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab);
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
||||
strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
||||
strPrintf(pSql, " AND ota_control LIKE '%%x%%'");
|
||||
}
|
||||
|
||||
@@ -1302,7 +1304,7 @@ static void rbudiff_one_table(const char *zTab, FILE *out){
|
||||
char *zOtaControl;
|
||||
int nOtaControl = sqlite3_column_bytes(pStmt, nCol);
|
||||
|
||||
zOtaControl = (char*)sqlite3_malloc(nOtaControl);
|
||||
zOtaControl = (char*)sqlite3_malloc(nOtaControl+1);
|
||||
memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1);
|
||||
|
||||
for(i=0; i<nCol; i++){
|
||||
|
||||
Reference in New Issue
Block a user