Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 425ab50c8f | |||
| 5eba9b73da | |||
| 34c5ed29e8 | |||
| e255f2d2ce | |||
| c3caa4f091 |
@@ -1,5 +1,5 @@
|
||||
C In\sthe\sWin32\sVFS,\sthe\swinSysInfo\svariable\sshould\sbe\sstatic.
|
||||
D 2014-02-05T11:05:47.687
|
||||
C Change\smore\sOP_OpenEphemeral\soperations\sto\sOP_OpenHash.
|
||||
D 2014-02-06T14:59:47.807
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -166,16 +166,16 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 7b2c3cd16deedff7f4904f2e871e7b77328b9872
|
||||
F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9
|
||||
F src/btree.c ae408b77caf025df8c1baab52aad8a5a393bed35
|
||||
F src/btree.h f1c65e0511d7a228fd1a7c7463ef842525364348
|
||||
F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
|
||||
F src/build.c 7e6c275ab1731510d6f793d0f88373ab3e858e69
|
||||
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 77779efbe78dd678d84bfb4fc2e87b6b6ad8dccd
|
||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||
F src/delete.c 6765a421f08adbedc5d52d21760ec6dbe5123fd3
|
||||
F src/expr.c 2c710c35a05486d8e2e142bff1e66bac6ed39a35
|
||||
F src/delete.c 3987ac3e32f6a447b1f3ca0268d67bdaf0a8b97f
|
||||
F src/expr.c fa9cd9b4bdc8989efce99d49c7e46484e17f41f4
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5
|
||||
F src/func.c f4499b39d66b71825514334ce67b32ff14bd19f5
|
||||
@@ -183,7 +183,7 @@ F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486
|
||||
F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c a4450f0c46a9f221622e6551ab0953b03c4f8ee8
|
||||
F src/insert.c c6b4ba486e62d0b600632fdfce4bca76213158cb
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
@@ -219,12 +219,12 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c b78f5e62c283aca2e38657938bc1fec1051df728
|
||||
F src/select.c d1acf45637f70d71a76cb8312507550e3aee6caa
|
||||
F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca
|
||||
F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h fdab572b3567d587cf3096e3ca5dabcedda3fa66
|
||||
F src/sqliteInt.h 29b97acb02309ad07de107a81c381512066fc2e4
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@@ -276,11 +276,11 @@ F src/test_vfstrace.c 3a0ab304682fecbceb689e7d9b904211fde11d78
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7
|
||||
F src/trigger.c 5c1c0b899ac0ce284763dcb8fdbaa38ecf15ef98
|
||||
F src/update.c a7df6fffce6bfedc578fda6136dd33e34a63f8ee
|
||||
F src/update.c 3d2bdfe24d78303cf7fd3017aaa2d848d47a1020
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c 15ac2627f548f5481d0d7e6c4eb67be673027695
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c e703913e9a3b56d7824f3eb8b76d99f496ff6dc1
|
||||
F src/vdbe.c 2e46c464e910cf0f11acf6690e8f72d7239a5f63
|
||||
F src/vdbe.h e6c4c610fcabad4fa80ebb1efc6822a9367e2b26
|
||||
F src/vdbeInt.h 42db251e9f863401ff847b90d5fe1614c89a6a56
|
||||
F src/vdbeapi.c ce4e68ea4842cc6081046f533d088dcf01d247ad
|
||||
@@ -293,7 +293,7 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
|
||||
F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
||||
F src/where.c bacb79fb31e082c9c599e68e5e9f161e1d5430ca
|
||||
F src/where.c 087307272e374c35c7eb1eb722f0ab7db09317f2
|
||||
F src/whereInt.h 921f935af8b684ffb49705610bda7284db1db138
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@@ -432,7 +432,7 @@ F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 44028aaf161a5e80a2f229622b3a174d3b352810
|
||||
F test/distinct.test b9f34f3827a67baf5b5bcb1cdf5185963d629d49
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test ee95d48664503d40f6cc9ef4a7d03216188e2ada
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
@@ -593,9 +593,9 @@ F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f
|
||||
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in3.test dbf41c0c073c10a8c0fee280cba3e9dddbd4a9c6
|
||||
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||
F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3
|
||||
F test/in5.test 9d8c15bfc9a06da5b354d4d4ecfea9f928769641
|
||||
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
|
||||
F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
|
||||
F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
|
||||
@@ -1152,7 +1152,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P f2504089df0bf4011864e67825b37f6aa3d03458
|
||||
R f919fd88b0fe2a17b41d9c03ff44720f
|
||||
U mistachkin
|
||||
Z fecb65c3944a970d4d7662e030506246
|
||||
P 715fac7749a6b1523fe9f7de8263f0c4d1571d07
|
||||
R f507eae713e5b72113d4535fdf641347
|
||||
U drh
|
||||
Z 759564bb3627cf7a97644b4104ef33de
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4a4dd371a72b7d475185923bebb4cd9bd83e1bd9
|
||||
881164cf6e5c987ae0e40804267715d878135198
|
||||
+7
-4
@@ -1527,7 +1527,7 @@ static int btreeInitPage(MemPage *pPage){
|
||||
** Set up a raw page so that it looks like a database page holding
|
||||
** no entries.
|
||||
*/
|
||||
static void zeroPage(MemPage *pPage, int flags){
|
||||
static void zeroPage(MemPage *pPage, u8 flags){
|
||||
unsigned char *data = pPage->aData;
|
||||
BtShared *pBt = pPage->pBt;
|
||||
u8 hdr = pPage->hdrOffset;
|
||||
@@ -2585,6 +2585,7 @@ static int newDatabase(BtShared *pBt){
|
||||
MemPage *pP1;
|
||||
unsigned char *data;
|
||||
int rc;
|
||||
u8 flags;
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
if( pBt->nPage>0 ){
|
||||
@@ -2607,7 +2608,9 @@ static int newDatabase(BtShared *pBt){
|
||||
data[22] = 32;
|
||||
data[23] = 32;
|
||||
memset(&data[24], 0, 100-24);
|
||||
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
|
||||
flags = (pBt->openFlags&BTREE_SINGLE_INDEX) ? PTF_ZERODATA|PTF_LEAF
|
||||
: PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF;
|
||||
zeroPage(pP1, flags);
|
||||
pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );
|
||||
@@ -6109,7 +6112,7 @@ static int balance_nonroot(
|
||||
u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */
|
||||
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
|
||||
int usableSpace; /* Bytes in pPage beyond the header */
|
||||
int pageFlags; /* Value of pPage->aData[0] */
|
||||
u8 pageFlags; /* Value of pPage->aData[0] */
|
||||
int subtotal; /* Subtotal of bytes in cells on one page */
|
||||
int iSpace1 = 0; /* First unused byte of aSpace1[] */
|
||||
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
|
||||
@@ -7210,7 +7213,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
MemPage *pRoot;
|
||||
Pgno pgnoRoot;
|
||||
int rc;
|
||||
int ptfFlags; /* Page-type flage for the root page of new table */
|
||||
u8 ptfFlags; /* Page-type flage for the root page of new table */
|
||||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
|
||||
+5
-4
@@ -56,10 +56,11 @@ int sqlite3BtreeOpen(
|
||||
** NOTE: These values must match the corresponding PAGER_ values in
|
||||
** pager.h.
|
||||
*/
|
||||
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
|
||||
#define BTREE_MEMORY 2 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */
|
||||
#define BTREE_OMIT_JOURNAL 0x01 /* Do not create or use a rollback journal */
|
||||
#define BTREE_MEMORY 0x02 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 0x04 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 0x08 /* Use of a hash implementation is OK */
|
||||
#define BTREE_SINGLE_INDEX 0x10 /* File contains one index btree */
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
|
||||
+3
-1
@@ -383,7 +383,9 @@ void sqlite3DeleteFrom(
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
}else{
|
||||
/* For a WITHOUT ROWID table, create an ephermeral table used to
|
||||
** hold all primary keys for rows to be deleted. */
|
||||
** hold all primary keys for rows to be deleted. Use OP_OpenEphemeral
|
||||
** rather than OP_OpenHash since for efficiency reasons it is good to
|
||||
** process the primary keys in order. */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
|
||||
+219
-212
@@ -1490,6 +1490,177 @@ int sqlite3CodeOnce(Parse *pParse){
|
||||
return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that constructs a transient table for the RHS of an IN
|
||||
** operator:
|
||||
**
|
||||
** x IN (4,5,11) -- IN operator with list on right-hand side
|
||||
** x IN (SELECT a FROM b) -- IN operator with subquery on the right
|
||||
**
|
||||
** The pExpr parameter is the IN expression.
|
||||
**
|
||||
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
|
||||
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
|
||||
** to some integer key column of a table B-Tree. In this case, use an
|
||||
** intkey B-Tree to store the set of IN(...) values instead of the usual
|
||||
** (slower) variable length keys B-Tree.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
static void sqlite3CreateInOperatorRhsTable(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
|
||||
int isRowid, /* If true, LHS of IN operator is a rowid */
|
||||
int bOrdered /* If true, must use btree, not a hash */
|
||||
){
|
||||
int testAddr = -1; /* One-time test address */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* prepared stmt under construction */
|
||||
char affinity; /* Affinity of the LHS of the IN */
|
||||
int addr; /* Address of OP_Open.. instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pExpr->op==TK_IN );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
|
||||
/* This code must be run in its entirety every time it is encountered
|
||||
** if any of the following is true:
|
||||
**
|
||||
** * The right-hand side is a correlated subquery
|
||||
** * We are inside a trigger
|
||||
**
|
||||
** If all of the above are false, then we can run this code just once
|
||||
** save the results, and reuse the same result on subsequent invocations.
|
||||
*/
|
||||
if( !ExprHasProperty(pExpr, EP_VarSelect) ){
|
||||
testAddr = sqlite3CodeOnce(pParse);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %sLIST SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** SELECT... statement are columns, then numeric affinity is used
|
||||
** if either column has NUMERIC or INTEGER affinity. If neither
|
||||
** 'x' nor the SELECT... statement are columns, then numeric affinity
|
||||
** is used.
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, bOrdered ? OP_OpenEphemeral : OP_OpenHash,
|
||||
pExpr->iTable, !isRowid);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
SelectDest dest;
|
||||
ExprList *pEList;
|
||||
|
||||
assert( !isRowid );
|
||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affSdst = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
return;
|
||||
}
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr>0 );
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
** store it in the temporary table. If <expr> is a column, then use
|
||||
** that columns affinity when building index keys. If <expr> is not
|
||||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
if( !affinity ){
|
||||
affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
}
|
||||
|
||||
/* Loop through each expression in <exprlist>. */
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
|
||||
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
|
||||
Expr *pE2 = pItem->pExpr;
|
||||
int iValToIns;
|
||||
|
||||
/* If the expression is not constant then we will need to
|
||||
** disable the test that was generated above that makes sure
|
||||
** this code only executes once. Because for a non-constant
|
||||
** expression we need to rerun this code each time.
|
||||
*/
|
||||
if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr);
|
||||
testAddr = -1;
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
|
||||
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
|
||||
}else{
|
||||
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
|
||||
if( isRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
|
||||
sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r3, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
|
||||
if( testAddr>=0 ){
|
||||
sqlite3VdbeJumpHere(v, testAddr);
|
||||
}
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
/*
|
||||
** This function is used by the implementation of the IN (...) operator.
|
||||
** The pX parameter is the expression on the RHS of the IN operator, which
|
||||
@@ -1553,15 +1724,19 @@ int sqlite3CodeOnce(Parse *pParse){
|
||||
**
|
||||
** in order to avoid running the <test if data structure contains null>
|
||||
** test more often than is necessary.
|
||||
**
|
||||
** IN_INDEX_EPH ephemeral tables must be in key order if the bOrdered flag
|
||||
** is true. If bOrdered is false, the generated table can be a hash.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound, int bOrdered){
|
||||
Select *p; /* SELECT to the right of IN operator */
|
||||
int eType = 0; /* Type of RHS table. IN_INDEX_* */
|
||||
int iTab = pParse->nTab++; /* Cursor of the RHS table */
|
||||
int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pX->op==TK_IN );
|
||||
|
||||
/* Check to see if an existing table or index can be used to
|
||||
@@ -1570,11 +1745,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
*/
|
||||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
i16 iCol; /* Index of column <column> */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
i16 iCol; /* Index of column <column> */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
|
||||
assert( p ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
@@ -1589,19 +1764,10 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
/* This function is only called from two places. In both cases the vdbe
|
||||
** has already been allocated. So assume sqlite3GetVdbe() is always
|
||||
** successful here.
|
||||
*/
|
||||
assert(v);
|
||||
if( iCol<0 ){
|
||||
int iAddr;
|
||||
|
||||
iAddr = sqlite3CodeOnce(pParse);
|
||||
|
||||
int iAddr = sqlite3CodeOnce(pParse);
|
||||
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
||||
eType = IN_INDEX_ROWID;
|
||||
|
||||
sqlite3VdbeJumpHere(v, iAddr);
|
||||
}else{
|
||||
Index *pIdx; /* Iterator variable */
|
||||
@@ -1656,7 +1822,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
eType = IN_INDEX_ROWID;
|
||||
}
|
||||
}
|
||||
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
|
||||
sqlite3CreateInOperatorRhsTable(pParse, pX, eType==IN_INDEX_ROWID,bOrdered);
|
||||
pParse->nQueryLoop = savedNQueryLoop;
|
||||
}else{
|
||||
pX->iTable = iTab;
|
||||
@@ -1666,56 +1832,37 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
|
||||
** or IN operators. Examples:
|
||||
** Generate code for scalar subqueries used as a subquery expression:
|
||||
**
|
||||
** (SELECT a FROM b) -- subquery
|
||||
** EXISTS (SELECT a FROM b) -- EXISTS subquery
|
||||
** x IN (4,5,11) -- IN operator with list on right-hand side
|
||||
** x IN (SELECT a FROM b) -- IN operator with subquery on the right
|
||||
**
|
||||
** The pExpr parameter describes the expression that contains the IN
|
||||
** operator or subquery.
|
||||
** The pExpr parameter describes the expression that contains the subquery.
|
||||
**
|
||||
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
|
||||
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
|
||||
** to some integer key column of a table B-Tree. In this case, use an
|
||||
** intkey B-Tree to store the set of IN(...) values instead of the usual
|
||||
** (slower) variable length keys B-Tree.
|
||||
**
|
||||
** If rMayHaveNull is non-zero, that means that the operation is an IN
|
||||
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
|
||||
** Furthermore, the IN is in a WHERE clause and that we really want
|
||||
** to iterate over the RHS of the IN operator in order to quickly locate
|
||||
** all corresponding LHS elements. All this routine does is initialize
|
||||
** the register given by rMayHaveNull to NULL. Calling routines will take
|
||||
** care of changing this register value to non-NULL if the RHS is NULL-free.
|
||||
**
|
||||
** If rMayHaveNull is zero, that means that the subquery is being used
|
||||
** for membership testing only. There is no need to initialize any
|
||||
** registers to indicate the presence or absence of NULLs on the RHS.
|
||||
**
|
||||
** For a SELECT or EXISTS operator, return the register that holds the
|
||||
** result. For IN operators or if an error occurs, the return value is 0.
|
||||
** Return the register that holds the result.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3CodeSubselect(
|
||||
static int sqlite3CodeScalarSubquery(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
|
||||
int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
|
||||
int isRowid /* If true, LHS of IN operator is a rowid */
|
||||
Expr *pExpr /* The SELECT or EXISTS operator */
|
||||
){
|
||||
int testAddr = -1; /* One-time test address */
|
||||
int rReg = 0; /* Register storing resulting */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
Select *pSel; /* SELECT statement to encode */
|
||||
SelectDest dest; /* How to deal with SELECt result */
|
||||
|
||||
if( NEVER(v==0) ) return 0;
|
||||
testcase( pExpr->op==TK_EXISTS );
|
||||
testcase( pExpr->op==TK_SELECT );
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
|
||||
/* This code must be run in its entirety every time it is encountered
|
||||
** if any of the following is true:
|
||||
**
|
||||
** * The right-hand side is a correlated subquery
|
||||
** * The right-hand side is an expression list containing variables
|
||||
** * pExpr is a correlated subquery
|
||||
** * We are inside a trigger
|
||||
**
|
||||
** If all of the above are false, then we can run this code just once
|
||||
@@ -1728,173 +1875,33 @@ int sqlite3CodeSubselect(
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
|
||||
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
|
||||
pParse->db, "EXECUTE %sSCALAR SUBQUERY %d",
|
||||
testAddr>=0?"":"CORRELATED ", pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_IN: {
|
||||
char affinity; /* Affinity of the LHS of the IN */
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||
|
||||
if( rMayHaveNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
|
||||
}
|
||||
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** SELECT... statement are columns, then numeric affinity is used
|
||||
** if either column has NUMERIC or INTEGER affinity. If neither
|
||||
** 'x' nor the SELECT... statement are columns, then numeric affinity
|
||||
** is used.
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
SelectDest dest;
|
||||
ExprList *pEList;
|
||||
|
||||
assert( !isRowid );
|
||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affSdst = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
return 0;
|
||||
}
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr>0 );
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
** store it in the temporary table. If <expr> is a column, then use
|
||||
** that columns affinity when building index keys. If <expr> is not
|
||||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
if( !affinity ){
|
||||
affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
}
|
||||
|
||||
/* Loop through each expression in <exprlist>. */
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
|
||||
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
|
||||
Expr *pE2 = pItem->pExpr;
|
||||
int iValToIns;
|
||||
|
||||
/* If the expression is not constant then we will need to
|
||||
** disable the test that was generated above that makes sure
|
||||
** this code only executes once. Because for a non-constant
|
||||
** expression we need to rerun this code each time.
|
||||
*/
|
||||
if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr);
|
||||
testAddr = -1;
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
|
||||
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
|
||||
}else{
|
||||
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
|
||||
if( isRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
|
||||
sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r3, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TK_EXISTS:
|
||||
case TK_SELECT:
|
||||
default: {
|
||||
/* If this has to be a scalar SELECT. Generate code to put the
|
||||
** value of this select in a memory cell and record the number
|
||||
** of the memory cell in iColumn. If this is an EXISTS, write
|
||||
** an integer 0 (not exists) or 1 (exists) into a memory cell
|
||||
** and record that memory cell in iColumn.
|
||||
*/
|
||||
Select *pSel; /* SELECT statement to encode */
|
||||
SelectDest dest; /* How to deal with SELECt result */
|
||||
|
||||
testcase( pExpr->op==TK_EXISTS );
|
||||
testcase( pExpr->op==TK_SELECT );
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init subquery result"));
|
||||
}else{
|
||||
dest.eDest = SRT_Exists;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
}
|
||||
sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
||||
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
|
||||
&sqlite3IntTokens[1]);
|
||||
pSel->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pSel, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
rReg = dest.iSDParm;
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
break;
|
||||
}
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init subquery result"));
|
||||
}else{
|
||||
dest.eDest = SRT_Exists;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
}
|
||||
sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
||||
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
|
||||
&sqlite3IntTokens[1]);
|
||||
pSel->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pSel, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
rReg = dest.iSDParm;
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
|
||||
if( testAddr>=0 ){
|
||||
sqlite3VdbeJumpHere(v, testAddr);
|
||||
@@ -1941,7 +1948,7 @@ static void sqlite3ExprCodeIN(
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 ); /* OOM detected prior to this routine */
|
||||
VdbeNoopComment((v, "begin IN expr"));
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull);
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull, 0);
|
||||
|
||||
/* Figure out the affinity to use to create a key from the results
|
||||
** of the expression. affinityStr stores a static string suitable for
|
||||
@@ -2813,7 +2820,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
case TK_SELECT: {
|
||||
testcase( op==TK_EXISTS );
|
||||
testcase( op==TK_SELECT );
|
||||
inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
|
||||
inReg = sqlite3CodeScalarSubquery(pParse, pExpr);
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
|
||||
@@ -719,6 +719,9 @@ void sqlite3Insert(
|
||||
** insert row from R..R+n into temp table
|
||||
** goto L
|
||||
** M: ...
|
||||
**
|
||||
** Use OP_OpenEphemeral, not OP_OpenHash, so that srcTab behaves as
|
||||
** a FIFO.
|
||||
*/
|
||||
int regRec; /* Register to hold packed record */
|
||||
int regTempRowid; /* Register to hold temp table ROWID */
|
||||
|
||||
+11
-10
@@ -535,8 +535,8 @@ typedef struct DistinctCtx DistinctCtx;
|
||||
struct DistinctCtx {
|
||||
u8 isTnct; /* True if the DISTINCT keyword is present */
|
||||
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
|
||||
int tabTnct; /* Ephemeral table used for DISTINCT processing */
|
||||
int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
|
||||
int tabTnct; /* Table containing previously seen values */
|
||||
int addrTnct; /* Address of OpenEphemeral/OpenHash opcode for tabTnct */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1871,7 +1871,7 @@ static void generateWithRecursiveQuery(
|
||||
}
|
||||
VdbeComment((v, "Queue table"));
|
||||
if( iDistinct ){
|
||||
p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
|
||||
p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenHash, iDistinct, 0);
|
||||
p->selFlags |= SF_UsesEphemeral;
|
||||
}
|
||||
|
||||
@@ -2007,8 +2007,7 @@ static int multiSelect(
|
||||
*/
|
||||
if( dest.eDest==SRT_EphemTab ){
|
||||
assert( p->pEList );
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenHash, dest.iSDParm, p->pEList->nExpr);
|
||||
dest.eDest = SRT_Table;
|
||||
}
|
||||
|
||||
@@ -2108,7 +2107,7 @@ static int multiSelect(
|
||||
*/
|
||||
unionTab = pParse->nTab++;
|
||||
assert( p->pOrderBy==0 );
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenHash, unionTab, 0);
|
||||
assert( p->addrOpenEphm[0] == -1 );
|
||||
p->addrOpenEphm[0] = addr;
|
||||
p->pRightmost->selFlags |= SF_UsesEphemeral;
|
||||
@@ -2191,7 +2190,8 @@ static int multiSelect(
|
||||
|
||||
/* INTERSECT is different from the others since it requires
|
||||
** two temporary tables. Hence it has its own case. Begin
|
||||
** by allocating the tables we will need.
|
||||
** by allocating the tables we will need. The tables must be
|
||||
** ordered: Use OP_OpenEphermeral, not OP_OpenHash.
|
||||
*/
|
||||
tab1 = pParse->nTab++;
|
||||
tab2 = pParse->nTab++;
|
||||
@@ -4302,7 +4302,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
pFunc->iDistinct = -1;
|
||||
}else{
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||
sqlite3VdbeAddOp4(v, OP_OpenHash, pFunc->iDistinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
}
|
||||
@@ -4721,6 +4721,8 @@ int sqlite3Select(
|
||||
}
|
||||
|
||||
/* If the output is destined for a temporary table, open that table.
|
||||
** Use OP_OpenEphemeral rather than OP_OpenHash to keep the rows in
|
||||
** their original order.
|
||||
*/
|
||||
if( pDest->eDest==SRT_EphemTab ){
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr);
|
||||
@@ -4740,11 +4742,10 @@ int sqlite3Select(
|
||||
*/
|
||||
if( p->selFlags & SF_Distinct ){
|
||||
sDistinct.tabTnct = pParse->nTab++;
|
||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenHash,
|
||||
sDistinct.tabTnct, 0, 0,
|
||||
(char*)keyInfoFromExprList(pParse, p->pEList, 0),
|
||||
P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
||||
}else{
|
||||
sDistinct.eTnctType = WHERE_DISTINCT_NOOP;
|
||||
|
||||
+1
-2
@@ -3283,7 +3283,6 @@ 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);
|
||||
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
int sqlite3ResolveExprNames(NameContext*, Expr*);
|
||||
@@ -3479,7 +3478,7 @@ const char *sqlite3JournalModename(int);
|
||||
#define IN_INDEX_EPH 2
|
||||
#define IN_INDEX_INDEX_ASC 3
|
||||
#define IN_INDEX_INDEX_DESC 4
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int*);
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int*, int);
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
|
||||
+1
-2
@@ -735,8 +735,7 @@ static void updateVirtualTable(
|
||||
*/
|
||||
assert( v );
|
||||
ephemTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenHash, ephemTab, pTab->nCol+1+(pRowid!=0));
|
||||
|
||||
/* fill the ephemeral table
|
||||
*/
|
||||
|
||||
+35
-30
@@ -3295,7 +3295,7 @@ case OP_OpenWrite: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 P5
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
**
|
||||
** Open a new cursor P1 to a transient table.
|
||||
@@ -3307,11 +3307,18 @@ case OP_OpenWrite: {
|
||||
** The cursor points to a BTree table if P4==0 and to a BTree index
|
||||
** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
|
||||
** that defines the format of keys in the index.
|
||||
*/
|
||||
/* Opcode: OpenHash P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
**
|
||||
** The P5 parameter can be a mask of the BTREE_* flags defined
|
||||
** in btree.h. These flags control aspects of the operation of
|
||||
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
|
||||
** added automatically.
|
||||
** Open a new cursor P1 to a transient table.
|
||||
** P2 is the number of columns in the ephemeral table.
|
||||
** The cursor points to a BTree table if P4==0 and to a BTree index
|
||||
** if P4 is a KeyInfo structure.
|
||||
**
|
||||
** This opcode is identical to OP_OpenEphemeral except that it
|
||||
** adds the BTREE_UNORDERED parameter to the sqlite3BtreeOpen() call,
|
||||
** thus causing the underlying table to unordered.
|
||||
*/
|
||||
/* Opcode: OpenAutoindex P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
@@ -3322,9 +3329,10 @@ case OP_OpenWrite: {
|
||||
** indices in joins.
|
||||
*/
|
||||
case OP_OpenAutoindex:
|
||||
case OP_OpenHash:
|
||||
case OP_OpenEphemeral: {
|
||||
VdbeCursor *pCx;
|
||||
KeyInfo *pKeyInfo;
|
||||
int btreeFlags;
|
||||
|
||||
static const int vfsFlags =
|
||||
SQLITE_OPEN_READWRITE |
|
||||
@@ -3334,38 +3342,35 @@ case OP_OpenEphemeral: {
|
||||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
assert( pOp->p1>=0 );
|
||||
assert( pOp->p2>=0 );
|
||||
assert( pOp->p5==0 );
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
|
||||
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
||||
btreeFlags = BTREE_OMIT_JOURNAL | BTREE_SINGLE;
|
||||
if( pOp->opcode==OP_OpenHash ){
|
||||
btreeFlags |= BTREE_UNORDERED;
|
||||
pCx->isOrdered = 0;
|
||||
}else{
|
||||
pCx->isOrdered = 1;
|
||||
}
|
||||
if( pOp->p4.pKeyInfo ){
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
assert( pOp->p4.pKeyInfo->db==db );
|
||||
assert( pOp->p4.pKeyInfo->enc==ENC(db) );
|
||||
btreeFlags |= BTREE_SINGLE_INDEX;
|
||||
pCx->isTable = 0;
|
||||
}else{
|
||||
pCx->isTable = 1;
|
||||
}
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt, btreeFlags, vfsFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If a transient index is required, create it by calling
|
||||
** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
|
||||
** opening it. If a transient table is required, just use the
|
||||
** automatically created table with root-page 1 (an BLOB_INTKEY table).
|
||||
*/
|
||||
if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
|
||||
int pgno;
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pgno==MASTER_ROOT+1 );
|
||||
assert( pKeyInfo->db==db );
|
||||
assert( pKeyInfo->enc==ENC(db) );
|
||||
pCx->pKeyInfo = pKeyInfo;
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
|
||||
}
|
||||
pCx->isTable = 0;
|
||||
}else{
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
|
||||
pCx->isTable = 1;
|
||||
}
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, pOp->p4.pKeyInfo,
|
||||
pCx->pCursor);
|
||||
}
|
||||
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -2348,16 +2348,17 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
|
||||
** this routine sets up a loop that will iterate over all values of X.
|
||||
*/
|
||||
static int codeEqualityTerm(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereInfo *pWInfo, /* WHERE clause */
|
||||
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
|
||||
WhereLevel *pLevel, /* The level of the FROM clause we are working on */
|
||||
int iEq, /* Index of the equality term within this level */
|
||||
int bRev, /* True for reverse-order IN operations */
|
||||
int iTarget /* Attempt to leave results in this register */
|
||||
){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int iReg; /* Register holding results */
|
||||
Expr *pX = pTerm->pExpr; /* Expression to be coded */
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
Vdbe *v = pParse->pVdbe; /* Prepared stmt under construction */
|
||||
int iReg; /* Register holding results */
|
||||
|
||||
assert( iTarget>0 );
|
||||
if( pX->op==TK_EQ ){
|
||||
@@ -2382,7 +2383,7 @@ static int codeEqualityTerm(
|
||||
}
|
||||
assert( pX->op==TK_IN );
|
||||
iReg = iTarget;
|
||||
eType = sqlite3FindInIndex(pParse, pX, 0);
|
||||
eType = sqlite3FindInIndex(pParse, pX, 0, pWInfo->bOBSat);
|
||||
if( eType==IN_INDEX_INDEX_DESC ){
|
||||
testcase( bRev );
|
||||
bRev = !bRev;
|
||||
@@ -2464,7 +2465,7 @@ static int codeEqualityTerm(
|
||||
** string in this example would be set to SQLITE_AFF_NONE.
|
||||
*/
|
||||
static int codeAllEqualityTerms(
|
||||
Parse *pParse, /* Parsing context */
|
||||
WhereInfo *pWInfo, /* WHERE clause */
|
||||
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
|
||||
int bRev, /* Reverse the order of IN operators */
|
||||
int nExtraReg, /* Number of extra registers to allocate */
|
||||
@@ -2472,6 +2473,7 @@ static int codeAllEqualityTerms(
|
||||
){
|
||||
u16 nEq; /* The number of == or IN constraints to code */
|
||||
u16 nSkip; /* Number of left-most columns to skip */
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
Vdbe *v = pParse->pVdbe; /* The vm under construction */
|
||||
Index *pIdx; /* The index being used for this loop */
|
||||
WhereTerm *pTerm; /* A single constraint term */
|
||||
@@ -2526,7 +2528,7 @@ static int codeAllEqualityTerms(
|
||||
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
|
||||
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
|
||||
r1 = codeEqualityTerm(pWInfo, pTerm, pLevel, j, bRev, regBase+j);
|
||||
if( r1!=regBase+j ){
|
||||
if( nReg==1 ){
|
||||
sqlite3ReleaseTempReg(pParse, regBase);
|
||||
@@ -2809,7 +2811,7 @@ static Bitmask codeOneLoopStart(
|
||||
pTerm = pLoop->aLTerm[j];
|
||||
if( pTerm==0 ) continue;
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
|
||||
codeEqualityTerm(pWInfo, pTerm, pLevel, j, bRev, iTarget);
|
||||
addrNotFound = pLevel->addrNxt;
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
|
||||
@@ -2849,7 +2851,7 @@ static Bitmask codeOneLoopStart(
|
||||
assert( pTerm->pExpr!=0 );
|
||||
assert( omitTable==0 );
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
|
||||
iRowidReg = codeEqualityTerm(pWInfo, pTerm, pLevel, 0, bRev, iReleaseReg);
|
||||
addrNxt = pLevel->addrNxt;
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
|
||||
@@ -3039,7 +3041,7 @@ static Bitmask codeOneLoopStart(
|
||||
** and store the values of those terms in an array of registers
|
||||
** starting at regBase.
|
||||
*/
|
||||
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
regBase = codeAllEqualityTerms(pWInfo,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
|
||||
if( zStartAff ) cEndAff = zStartAff[nEq];
|
||||
addrNxt = pLevel->addrNxt;
|
||||
|
||||
+3
-6
@@ -51,12 +51,9 @@ proc do_temptables_test {tn sql temptables} {
|
||||
set ret ""
|
||||
db eval "EXPLAIN [set sql]" {
|
||||
if {$opcode == "OpenEphemeral" || $opcode == "SorterOpen"} {
|
||||
if {$p5 != "08" && $p5!="00"} { error "p5 = $p5" }
|
||||
if {$p5 == "08"} {
|
||||
lappend ret hash
|
||||
} else {
|
||||
lappend ret btree
|
||||
}
|
||||
lappend ret btree
|
||||
} elseif {$opcode == "OpenHash"} {
|
||||
lappend ret hash
|
||||
}
|
||||
}
|
||||
set ret
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ ifcapable !subquery {
|
||||
proc nEphemeral {sql} {
|
||||
set nEph 0
|
||||
foreach op [execsql "EXPLAIN $sql"] {
|
||||
if {$op eq "OpenEphemeral"} {incr nEph}
|
||||
if {$op eq "OpenEphemeral" || $op eq "OpenHash"} {incr nEph}
|
||||
}
|
||||
set nEph
|
||||
}
|
||||
|
||||
+3
-3
@@ -65,17 +65,17 @@ do_test in5-2.4 {
|
||||
}
|
||||
} {12a 56e}
|
||||
do_test in5-2.5.1 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t3x AND b IN t1y AND c IN t1z
|
||||
}]
|
||||
} {1}
|
||||
do_test in5-2.5.2 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t3y AND c IN t1z
|
||||
}]
|
||||
} {1}
|
||||
do_test in5-2.5.3 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t3z
|
||||
}]
|
||||
} {1}
|
||||
|
||||
Reference in New Issue
Block a user