aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c42
-rw-r--r--src/btree.h54
-rw-r--r--src/delete.c5
-rw-r--r--src/expr.c46
-rw-r--r--src/insert.c7
-rw-r--r--src/loadext.c2
-rw-r--r--src/main.c45
-rw-r--r--src/malloc.c13
-rw-r--r--src/mem1.c5
-rw-r--r--src/mem3.c2
-rw-r--r--src/mem5.c11
-rw-r--r--src/pager.c41
-rw-r--r--src/pager.h1
-rw-r--r--src/parse.y19
-rw-r--r--src/pcache1.c4
-rw-r--r--src/pragma.c2
-rw-r--r--src/select.c15
-rw-r--r--src/sqlite.h.in33
-rw-r--r--src/sqliteInt.h14
-rw-r--r--src/status.c38
-rw-r--r--src/test1.c29
-rw-r--r--src/test3.c47
-rw-r--r--src/test_config.c6
-rw-r--r--src/tokenize.c2
-rw-r--r--src/update.c4
-rw-r--r--src/vdbe.c143
-rw-r--r--src/vdbe.h4
-rw-r--r--src/vdbeInt.h14
-rw-r--r--src/vdbeaux.c154
-rw-r--r--src/vdbemem.c7
-rw-r--r--src/vdbesort.c2
-rw-r--r--src/where.c53
-rw-r--r--src/wherecode.c177
33 files changed, 846 insertions, 195 deletions
diff --git a/src/btree.c b/src/btree.c
index 2c1a9983e..8d183efb3 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -858,6 +858,26 @@ int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow){
return SQLITE_OK;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Provide hints to the cursor. The particular hint given (and the type
+** and number of the varargs parameters) is determined by the eHintType
+** parameter. See the definitions of the BTREE_HINT_* macros for details.
+*/
+void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
+ /* Used only by system that substitute their own storage engine */
+}
+#endif
+
+/*
+** Provide flag hints to the cursor.
+*/
+void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
+ assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
+ pCur->hints = x;
+}
+
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
@@ -4055,13 +4075,16 @@ static int btreeCursor(
BtCursor *pX; /* Looping over other all cursors */
assert( sqlite3BtreeHoldsMutex(p) );
- assert( wrFlag==0 || wrFlag==1 );
+ assert( wrFlag==0
+ || wrFlag==BTREE_WRCSR
+ || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE)
+ );
/* The following assert statements verify that if this is a sharable
** b-tree database, the connection is holding the required table locks,
** and that no other connection has any open cursor that conflicts with
** this lock. */
- assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) );
+ assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) );
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
/* Assert that the caller has opened the required transaction. */
@@ -4086,8 +4109,7 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- assert( wrFlag==0 || wrFlag==BTCF_WriteFlag );
- pCur->curFlags = wrFlag;
+ pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0;
pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY;
/* If there are two or more cursors on the same btree, then all such
** cursors *must* have the BTCF_Multiple flag set. */
@@ -4499,7 +4521,9 @@ static int accessPayload(
/* If required, populate the overflow page-list cache. */
if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
- assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
+ assert( pCur->aOverflow[iIdx]==0
+ || pCur->aOverflow[iIdx]==nextPage
+ || CORRUPT_DB );
pCur->aOverflow[iIdx] = nextPage;
}
@@ -9622,14 +9646,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
return rc;
}
-/*
-** set the mask of hint flags for cursor pCsr.
-*/
-void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
- assert( mask==BTREE_BULKLOAD || mask==BTREE_SEEK_EQ || mask==0 );
- pCsr->hints = mask;
-}
-
#ifdef SQLITE_DEBUG
/*
** Return true if the cursor has a hint specified. This routine is
diff --git a/src/btree.h b/src/btree.h
index f7e92a260..f37ec5e7f 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -150,8 +150,37 @@ int sqlite3BtreeNewDb(Btree *p);
#define BTREE_DATA_VERSION 15 /* A virtual meta-value */
/*
-** Values that may be OR'd together to form the second argument of an
-** sqlite3BtreeCursorHints() call.
+** Kinds of hints that can be passed into the sqlite3BtreeCursorHint()
+** interface.
+**
+** BTREE_HINT_RANGE (arguments: Expr*, Mem*)
+**
+** The first argument is an Expr* (which is guaranteed to be constant for
+** the lifetime of the cursor) that defines constraints on which rows
+** might be fetched with this cursor. The Expr* tree may contain
+** TK_REGISTER nodes that refer to values stored in the array of registers
+** passed as the second parameter. In other words, if Expr.op==TK_REGISTER
+** then the value of the node is the value in Mem[pExpr.iTable]. Any
+** TK_COLUMN node in the expression tree refers to the Expr.iColumn-th
+** column of the b-tree of the cursor. The Expr tree will not contain
+** any function calls nor subqueries nor references to b-trees other than
+** the cursor being hinted.
+**
+** The design of the _RANGE hint is aid b-tree implementations that try
+** to prefetch content from remote machines - to provide those
+** implementations with limits on what needs to be prefetched and thereby
+** reduce network bandwidth.
+**
+** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
+** standard SQLite. The other hints are provided for extentions that use
+** the SQLite parser and code generator but substitute their own storage
+** engine.
+*/
+#define BTREE_HINT_RANGE 0 /* Range constraints on queries */
+
+/*
+** Values that may be OR'd together to form the argument to the
+** BTREE_HINT_FLAGS hint for sqlite3BtreeCursorHint():
**
** The BTREE_BULKLOAD flag is set on index cursors when the index is going
** to be filled with content that is already in sorted order.
@@ -165,6 +194,22 @@ int sqlite3BtreeNewDb(Btree *p);
#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */
#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */
+/*
+** Flags passed as the third argument to sqlite3BtreeCursor().
+**
+** For read-only cursors the wrFlag argument is always zero. For read-write
+** cursors it may be set to either (BTREE_WRCSR|BTREE_FORDELETE) or
+** (BTREE_WRCSR). If the BTREE_FORDELETE flag is set, then the cursor will
+** only be used by SQLite for the following:
+**
+** * to seek to and delete specific entries, and/or
+**
+** * to read values that will be used to create keys that other
+** BTREE_FORDELETE cursors will seek to and delete.
+*/
+#define BTREE_WRCSR 0x00000004 /* read-write cursor */
+#define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */
+
int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
int iTable, /* Index of root page */
@@ -174,6 +219,10 @@ int sqlite3BtreeCursor(
);
int sqlite3BtreeCursorSize(void);
void sqlite3BtreeCursorZero(BtCursor*);
+void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+void sqlite3BtreeCursorHint(BtCursor*, int, ...);
+#endif
int sqlite3BtreeCloseCursor(BtCursor*);
int sqlite3BtreeMovetoUnpacked(
@@ -208,7 +257,6 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeIncrblobCursor(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *);
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
-void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);
#ifdef SQLITE_DEBUG
int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
#endif
diff --git a/src/delete.c b/src/delete.c
index 9abefda1a..506c2af1f 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -481,12 +481,13 @@ void sqlite3DeleteFrom(
*/
if( !isView ){
int iAddrOnce = 0;
+ u8 p5 = (eOnePass==ONEPASS_OFF ? 0 : OPFLAG_FORDELETE);
if( eOnePass==ONEPASS_MULTI ){
iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
}
testcase( IsVirtual(pTab) );
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
- &iDataCur, &iIdxCur);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, p5, iTabCur,
+ aToOpen, &iDataCur, &iIdxCur);
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
diff --git a/src/expr.c b/src/expr.c
index b39dea2ea..6371e15ca 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -548,11 +548,11 @@ Expr *sqlite3PExpr(
const Token *pToken /* Argument token */
){
Expr *p;
- if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){
+ if( op==TK_AND && pParse->nErr==0 ){
/* Take advantage of short-circuit false optimization for AND */
p = sqlite3ExprAnd(pParse->db, pLeft, pRight);
}else{
- p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
+ p = sqlite3ExprAlloc(pParse->db, op & TKFLG_MASK, pToken, 1);
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
}
if( p ) {
@@ -1415,6 +1415,22 @@ int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
return exprIsConst(p, 4+isInit, 0);
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Walk an expression tree. Return 1 if the expression contains a
+** subquery of some kind. Return 0 if there are no subqueries.
+*/
+int sqlite3ExprContainsSubquery(Expr *p){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.eCode = 1;
+ w.xExprCallback = sqlite3ExprWalkNoop;
+ w.xSelectCallback = selectNodeIsConstant;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode==0;
+}
+#endif
+
/*
** If the expression p codes a constant integer that is small enough
** to fit in a 32-bit integer, return 1 and put the value of the integer
@@ -2481,9 +2497,12 @@ void sqlite3ExprCodeGetColumnOfTable(
/*
** Generate code that will extract the iColumn-th column from
-** table pTab and store the column value in a register. An effort
-** is made to store the column value in register iReg, but this is
-** not guaranteed. The location of the column value is returned.
+** table pTab and store the column value in a register.
+**
+** An effort is made to store the column value in register iReg. This
+** is not garanteeed for GetColumn() - the result can be stored in
+** any register. But the result is guaranteed to land in register iReg
+** for GetColumnToReg().
**
** There must be an open cursor to pTab in iTable when this routine
** is called. If iColumn<0 then code is generated that extracts the rowid.
@@ -2494,7 +2513,7 @@ int sqlite3ExprCodeGetColumn(
int iColumn, /* Index of the table column */
int iTable, /* The cursor pointing to the table */
int iReg, /* Store results here */
- u8 p5 /* P5 value for OP_Column */
+ u8 p5 /* P5 value for OP_Column + FLAGS */
){
Vdbe *v = pParse->pVdbe;
int i;
@@ -2516,6 +2535,17 @@ int sqlite3ExprCodeGetColumn(
}
return iReg;
}
+void sqlite3ExprCodeGetColumnToReg(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* Description of the table we are reading from */
+ int iColumn, /* Index of the table column */
+ int iTable, /* The cursor pointing to the table */
+ int iReg /* Store results here */
+){
+ int r1 = sqlite3ExprCodeGetColumn(pParse, pTab, iColumn, iTable, iReg, 0);
+ if( r1!=iReg ) sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, r1, iReg);
+}
+
/*
** Clear all column cache entries.
@@ -3342,6 +3372,10 @@ void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
**
** The SQLITE_ECEL_FACTOR argument allows constant arguments to be
** factored out into initialization code.
+**
+** The SQLITE_ECEL_REF flag means that expressions in the list with
+** ExprList.a[].u.x.iOrderByCol>0 have already been evaluated and stored
+** in registers at srcReg, and so the value can be copied from there.
*/
int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */
diff --git a/src/insert.c b/src/insert.c
index 95b291f83..f4af3391d 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -762,7 +762,7 @@ void sqlite3Insert(
/* If this is not a view, open the table and and all indices */
if( !isView ){
int nIdx;
- nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, 0,
+ nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
&iDataCur, &iIdxCur);
aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
if( aRegIdx==0 ){
@@ -1431,7 +1431,7 @@ void sqlite3GenerateConstraintChecks(
}else{
x = iField + regNewData + 1;
}
- sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
+ sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i);
VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
}
}
@@ -1659,6 +1659,7 @@ int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
int op, /* OP_OpenRead or OP_OpenWrite */
+ u8 p5, /* P5 value for OP_Open* instructions */
int iBase, /* Use this for the table cursor, if there is one */
u8 *aToOpen, /* If not NULL: boolean for each table and index */
int *piDataCur, /* Write the database source cursor number here */
@@ -1671,6 +1672,7 @@ int sqlite3OpenTableAndIndices(
Vdbe *v;
assert( op==OP_OpenRead || op==OP_OpenWrite );
+ assert( op==OP_OpenWrite || p5==0 );
if( IsVirtual(pTab) ){
/* This routine is a no-op for virtual tables. Leave the output
** variables *piDataCur and *piIdxCur uninitialized so that valgrind
@@ -1698,6 +1700,7 @@ int sqlite3OpenTableAndIndices(
if( aToOpen==0 || aToOpen[i+1] ){
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ sqlite3VdbeChangeP5(v, p5);
VdbeComment((v, "%s", pIdx->zName));
}
}
diff --git a/src/loadext.c b/src/loadext.c
index 1ae87d6b7..b6d50e96c 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -615,7 +615,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
** dummy pointer.
*/
#ifdef SQLITE_OMIT_LOAD_EXTENSION
-static const sqlite3_api_routines sqlite3Apis;
+static const sqlite3_api_routines sqlite3Apis = { 0 };
#endif
diff --git a/src/main.c b/src/main.c
index f41ea3b65..9d0b47874 100644
--- a/src/main.c
+++ b/src/main.c
@@ -741,6 +741,36 @@ int sqlite3_db_release_memory(sqlite3 *db){
}
/*
+** Flush any dirty pages in the pager-cache for any attached database
+** to disk.
+*/
+int sqlite3_db_cacheflush(sqlite3 *db){
+ int i;
+ int rc = SQLITE_OK;
+ int bSeenBusy = 0;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ Pager *pPager = sqlite3BtreePager(pBt);
+ rc = sqlite3PagerFlush(pPager);
+ if( rc==SQLITE_BUSY ){
+ bSeenBusy = 1;
+ rc = SQLITE_OK;
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return ((rc==SQLITE_OK && bSeenBusy) ? SQLITE_BUSY : rc);
+}
+
+/*
** Configuration settings for an individual database connection
*/
int sqlite3_db_config(sqlite3 *db, int op, ...){
@@ -2976,6 +3006,21 @@ opendb_out:
sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
}
#endif
+#if defined(SQLITE_HAS_CODEC)
+ if( rc==SQLITE_OK ){
+ const char *zHexKey = sqlite3_uri_parameter(zOpen, "hexkey");
+ if( zHexKey && zHexKey[0] ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zHexKey[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zHexKey[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ sqlite3_key_v2(db, 0, zKey, i/2);
+ }
+ }
+#endif
return rc & 0xff;
}
diff --git a/src/malloc.c b/src/malloc.c
index f20eb6e79..a4968aaa2 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -131,9 +131,7 @@ int sqlite3MallocInit(void){
sqlite3MemSetDefault();
}
memset(&mem0, 0, sizeof(mem0));
- if( sqlite3GlobalConfig.bCoreMutex ){
- mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
- }
+ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
&& sqlite3GlobalConfig.nScratch>0 ){
int i, n, sz;
@@ -224,7 +222,7 @@ static int mallocWithAlarm(int n, void **pp){
void *p;
assert( sqlite3_mutex_held(mem0.mutex) );
nFull = sqlite3GlobalConfig.m.xRoundup(n);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
if( mem0.alarmThreshold>0 ){
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.alarmThreshold - nFull ){
@@ -316,7 +314,7 @@ void *sqlite3ScratchMalloc(int n){
assert( n>0 );
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
p = mem0.pScratchFree;
mem0.pScratchFree = mem0.pScratchFree->pNext;
@@ -411,6 +409,7 @@ int sqlite3MallocSize(void *p){
return sqlite3GlobalConfig.m.xSize(p);
}
int sqlite3DbMallocSize(sqlite3 *db, void *p){
+ assert( p!=0 );
if( db==0 || !isLookaside(db,p) ){
#if SQLITE_DEBUG
if( db==0 ){
@@ -430,7 +429,7 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
sqlite3_uint64 sqlite3_msize(void *p){
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- return (sqlite3_uint64)sqlite3GlobalConfig.m.xSize(p);
+ return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
}
/*
@@ -518,7 +517,7 @@ void *sqlite3Realloc(void *pOld, u64 nBytes){
pNew = pOld;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
nDiff = nNew - nOld;
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
mem0.alarmThreshold-nDiff ){
diff --git a/src/mem1.c b/src/mem1.c
index ec9a4e3a6..b960ccfd4 100644
--- a/src/mem1.c
+++ b/src/mem1.c
@@ -172,10 +172,11 @@ static void sqlite3MemFree(void *pPrior){
*/
static int sqlite3MemSize(void *pPrior){
#ifdef SQLITE_MALLOCSIZE
- return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
+ assert( pPrior!=0 );
+ return (int)SQLITE_MALLOCSIZE(pPrior);
#else
sqlite3_int64 *p;
- if( pPrior==0 ) return 0;
+ assert( pPrior!=0 );
p = (sqlite3_int64*)pPrior;
p--;
return (int)p[0];
diff --git a/src/mem3.c b/src/mem3.c
index 1a1b791f2..2de028daa 100644
--- a/src/mem3.c
+++ b/src/mem3.c
@@ -476,7 +476,7 @@ static void memsys3FreeUnsafe(void *pOld){
*/
static int memsys3Size(void *p){
Mem3Block *pBlock;
- if( p==0 ) return 0;
+ assert( p!=0 );
pBlock = (Mem3Block*)p;
assert( (pBlock[-1].u.hdr.size4x&1)!=0 );
return (pBlock[-1].u.hdr.size4x&~3)*2 - 4;
diff --git a/src/mem5.c b/src/mem5.c
index 1479ddd0d..6bb24e544 100644
--- a/src/mem5.c
+++ b/src/mem5.c
@@ -200,12 +200,11 @@ static void memsys5Leave(void){
** works for chunks that are currently checked out.
*/
static int memsys5Size(void *p){
- int iSize = 0;
- if( p ){
- int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
- assert( i>=0 && i<mem5.nBlock );
- iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
- }
+ int iSize, i;
+ assert( p!=0 );
+ i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
+ assert( i>=0 && i<mem5.nBlock );
+ iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
return iSize;
}
diff --git a/src/pager.c b/src/pager.c
index 399070af0..314f792aa 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -4473,6 +4473,25 @@ static int pagerStress(void *p, PgHdr *pPg){
return pager_error(pPager, rc);
}
+/*
+** Flush all unreferenced dirty pages to disk.
+*/
+int sqlite3PagerFlush(Pager *pPager){
+ int rc = pPager->errCode;
+ if( !MEMDB ){
+ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
+ assert( assert_pager_state(pPager) );
+ while( rc==SQLITE_OK && pList ){
+ PgHdr *pNext = pList->pDirty;
+ if( pList->nRef==0 ){
+ rc = pagerStress((void*)pPager, pList);
+ }
+ pList = pNext;
+ }
+ }
+
+ return rc;
+}
/*
** Allocate and initialize a new Pager object and put a pointer to it
@@ -5896,9 +5915,10 @@ int sqlite3PagerWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
assert( (pPg->flags & PGHDR_MMAP)==0 );
assert( pPager->eState>=PAGER_WRITER_LOCKED );
- assert( pPager->eState!=PAGER_ERROR );
assert( assert_pager_state(pPager) );
- if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){
+ if( pPager->errCode ){
+ return pPager->errCode;
+ }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){
if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg);
return SQLITE_OK;
}else if( pPager->sectorSize > (u32)pPager->pageSize ){
@@ -6076,14 +6096,17 @@ int sqlite3PagerSync(Pager *pPager, const char *zMaster){
** returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager){
- int rc = SQLITE_OK;
- assert( pPager->eState==PAGER_WRITER_CACHEMOD
- || pPager->eState==PAGER_WRITER_DBMOD
- || pPager->eState==PAGER_WRITER_LOCKED
- );
+ int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
- if( 0==pagerUseWal(pPager) ){
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ if( rc==SQLITE_OK ){
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
+ || pPager->eState==PAGER_WRITER_DBMOD
+ || pPager->eState==PAGER_WRITER_LOCKED
+ );
+ assert( assert_pager_state(pPager) );
+ if( 0==pagerUseWal(pPager) ){
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
}
return rc;
}
diff --git a/src/pager.h b/src/pager.h
index 99a7aebc7..8e0e942ef 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -132,6 +132,7 @@ int sqlite3PagerGetJournalMode(Pager*);
int sqlite3PagerOkToChangeJournalMode(Pager*);
i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
+int sqlite3PagerFlush(Pager*);
/* Functions used to obtain and release page references. */
int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
diff --git a/src/parse.y b/src/parse.y
index e99feeefc..797fa9bde 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -929,6 +929,13 @@ term(A) ::= CTIME_KW(OP). {
pOut->zStart = pLeft->zStart;
pOut->zEnd = pRight->zEnd;
}
+
+ /* If doNot is true, then add a TK_NOT Expr-node wrapper around the
+ ** outside of *ppExpr.
+ */
+ static void exprNot(Parse *pParse, int doNot, Expr **ppExpr){
+ if( doNot ) *ppExpr = sqlite3PExpr(pParse, TK_NOT, *ppExpr, 0, 0);
+ }
}
expr(A) ::= expr(X) AND(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
@@ -951,7 +958,7 @@ expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
- if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, OP.bNot, &A.pExpr);
A.zStart = X.zStart;
A.zEnd = Y.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
@@ -962,7 +969,7 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
- if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, OP.bNot, &A.pExpr);
A.zStart = X.zStart;
A.zEnd = E.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
@@ -1052,7 +1059,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( N ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, N, &A.pExpr);
A.zStart = W.zStart;
A.zEnd = Y.zEnd;
}
@@ -1107,7 +1114,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
}else{
sqlite3ExprListDelete(pParse->db, Y);
}
- if( N ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, N, &A.pExpr);
}
A.zStart = X.zStart;
A.zEnd = &E.z[E.n];
@@ -1133,7 +1140,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
}else{
sqlite3SelectDelete(pParse->db, Y);
}
- if( N ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, N, &A.pExpr);
A.zStart = X.zStart;
A.zEnd = &E.z[E.n];
}
@@ -1147,7 +1154,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
}else{
sqlite3SrcListDelete(pParse->db, pSrc);
}
- if( N ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
+ exprNot(pParse, N, &A.pExpr);
A.zStart = X.zStart;
A.zEnd = Z.z ? &Z.z[Z.n] : &Y.z[Y.n];
}
diff --git a/src/pcache1.c b/src/pcache1.c
index ee6ac0b95..256e53a57 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -320,7 +320,7 @@ static void *pcache1Alloc(int nByte){
pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 );
- sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
}
sqlite3_mutex_leave(pcache1.mutex);
@@ -334,7 +334,7 @@ static void *pcache1Alloc(int nByte){
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
diff --git a/src/pragma.c b/src/pragma.c
index 64614a7eb..14c481174 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1465,7 +1465,7 @@ void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
sqlite3ExprCacheClear(pParse);
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead,
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
diff --git a/src/select.c b/src/select.c
index fad46f0b4..8db983891 100644
--- a/src/select.c
+++ b/src/select.c
@@ -4452,7 +4452,7 @@ static int selectExpander(Walker *pWalker, Select *p){
** Walker.xSelectCallback is set to do something useful for every
** subquery in the parser tree.
*/
-static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
+int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
UNUSED_PARAMETER2(NotUsed, NotUsed2);
return WRC_Continue;
}
@@ -4473,7 +4473,7 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
- w.xExprCallback = exprWalkNoop;
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
if( pParse->hasCompound ){
w.xSelectCallback = convertCompoundSelectToSubquery;
@@ -4540,7 +4540,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
w.xSelectCallback2 = selectAddSubqueryTypeInfo;
- w.xExprCallback = exprWalkNoop;
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
sqlite3WalkSelect(&w, pSelect);
#endif
@@ -5295,13 +5295,8 @@ int sqlite3Select(
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
if( pCol->iSorterColumn>=j ){
int r1 = j + regBase;
- int r2;
-
- r2 = sqlite3ExprCodeGetColumn(pParse,
- pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
- if( r1!=r2 ){
- sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
- }
+ sqlite3ExprCodeGetColumnToReg(pParse,
+ pCol->pTab, pCol->iColumn, pCol->iTable, r1);
j++;
}
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index a5d4e9487..ff5cf27ec 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -478,6 +478,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
+#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@@ -6572,7 +6573,8 @@ int sqlite3_status64(
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>This parameter records the deepest parser stack. It is only
+** <dd>The *pHighwater parameter records the deepest parser stack.
+** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@@ -7790,6 +7792,35 @@ int sqlite3_stmt_scanstatus(
*/
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+/*
+** CAPI3REF: Flush caches to disk mid-transaction
+**
+** If a write-transaction is open when this function is called, any dirty
+** pages in the pager-cache that are not currently in use are written out
+** to disk. A dirty page may be in use if a database cursor created by an
+** active SQL statement is reading from it, or if it is page 1 of a database
+** file (page 1 is always "in use"). Dirty pages are flushed for all
+** databases - "main", "temp" and any attached databases.
+**
+** If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. If said locks cannot be obtained
+** immediately and there is a busy-handler callback configured, it is invoked
+** in the usual manner. If the required lock still cannot be obtained, then
+** the database is skipped and an attempt made to flush any dirty pages
+** belonging to the next (if any) database. If any databases are skipped
+** because locks cannot be obtained, but no other error occurs, this
+** function returns SQLITE_BUSY.
+**
+** If any other error occurs while flushing dirty pages to disk (for
+** example an IO error or out-of-memory condition), then processing is
+** abandoned and an SQLite error code returned to the caller immediately.
+**
+** Otherwise, if no error occurs, SQLITE_OK is returned.
+**
+** This function does not set the database handle error code or message
+** returned by the sqlite3_errcode() and sqlite3_errmsg() functions.
+*/
+int sqlite3_db_cacheflush(sqlite3*);
/*
** CAPI3REF: The pre-update hook.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 0c43192ab..1ee31d0f3 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1318,6 +1318,7 @@ struct sqlite3 {
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
+#define SQLITE_CursorHints 0x2000 /* Add OP_CursorHint opcodes */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -2806,7 +2807,8 @@ struct AuthContext {
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */
-#define OPFLAG_P2ISREG 0x04 /* P2 to OP_Open** is a register number */
+#define OPFLAG_FORDELETE 0x08 /* OP_Open is opening for-delete csr */
+#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
/*
@@ -3035,6 +3037,7 @@ struct Walker {
int iCur; /* A cursor number */
SrcList *pSrcList; /* FROM clause */
struct SrcCount *pSrcCount; /* Counting column references */
+ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
} u;
};
@@ -3044,6 +3047,7 @@ int sqlite3WalkExprList(Walker*, ExprList*);
int sqlite3WalkSelect(Walker*, Select*);
int sqlite3WalkSelectExpr(Walker*, Select*);
int sqlite3WalkSelectFrom(Walker*, Select*);
+int sqlite3ExprWalkNoop(Walker*, Expr*);
/*
** Return code from the parse-tree walking primitives and their
@@ -3221,7 +3225,7 @@ const sqlite3_mem_methods *sqlite3MemGetMemsys5(void);
sqlite3_int64 sqlite3StatusValue(int);
void sqlite3StatusUp(int, int);
void sqlite3StatusDown(int, int);
-void sqlite3StatusSet(int, int);
+void sqlite3StatusHighwater(int, int);
/* Access to mutexes used by sqlite3_status() */
sqlite3_mutex *sqlite3Pcache1Mutex(void);
@@ -3405,6 +3409,7 @@ int sqlite3WhereOkOnePass(WhereInfo*, int*);
#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
+void sqlite3ExprCodeGetColumnToReg(Parse*, Table*, int, int, int);
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
void sqlite3ExprCodeMove(Parse*, int, int, int);
void sqlite3ExprCacheStore(Parse*, int, int, int);
@@ -3459,6 +3464,9 @@ int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*, u8);
int sqlite3ExprIsTableConstant(Expr*,int);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+int sqlite3ExprContainsSubquery(Expr*);
+#endif
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3ExprCanBeNull(const Expr*);
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
@@ -3471,7 +3479,7 @@ void sqlite3ResolvePartIdxLabel(Parse*,int);
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
u8,u8,int,int*);
void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
-int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*);
+int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*);
void sqlite3BeginWriteOperation(Parse*, int, int);
void sqlite3MultiWrite(Parse*);
void sqlite3MayAbort(Parse*);
diff --git a/src/status.c b/src/status.c
index ac32753a2..69f92ff7c 100644
--- a/src/status.c
+++ b/src/status.c
@@ -19,15 +19,15 @@
/*
** Variables in which to record status information.
*/
-typedef struct sqlite3StatType sqlite3StatType;
-static SQLITE_WSD struct sqlite3StatType {
#if SQLITE_PTRSIZE>4
- sqlite3_int64 nowValue[10]; /* Current value */
- sqlite3_int64 mxValue[10]; /* Maximum value */
+typedef sqlite3_int64 sqlite3StatValueType;
#else
- u32 nowValue[10]; /* Current value */
- u32 mxValue[10]; /* Maximum value */
+typedef u32 sqlite3StatValueType;
#endif
+typedef struct sqlite3StatType sqlite3StatType;
+static SQLITE_WSD struct sqlite3StatType {
+ sqlite3StatValueType nowValue[10]; /* Current value */
+ sqlite3StatValueType mxValue[10]; /* Maximum value */
} sqlite3Stat = { {0,}, {0,} };
/*
@@ -108,18 +108,24 @@ void sqlite3StatusDown(int op, int N){
}
/*
-** Set the value of a status to X. The highwater mark is adjusted if
-** necessary. The caller must hold the appropriate mutex.
+** Adjust the highwater mark if necessary.
+** The caller must hold the appropriate mutex.
*/
-void sqlite3StatusSet(int op, int X){
+void sqlite3StatusHighwater(int op, int X){
+ sqlite3StatValueType newValue;
wsdStatInit;
+ assert( X>=0 );
+ newValue = (sqlite3StatValueType)X;
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
assert( op>=0 && op<ArraySize(statMutex) );
assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
: sqlite3MallocMutex()) );
- wsdStat.nowValue[op] = X;
- if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){
- wsdStat.mxValue[op] = wsdStat.nowValue[op];
+ assert( op==SQLITE_STATUS_MALLOC_SIZE
+ || op==SQLITE_STATUS_PAGECACHE_SIZE
+ || op==SQLITE_STATUS_SCRATCH_SIZE
+ || op==SQLITE_STATUS_PARSER_STACK );
+ if( newValue>wsdStat.mxValue[op] ){
+ wsdStat.mxValue[op] = newValue;
}
}
@@ -252,10 +258,10 @@ int sqlite3_db_status(
+ pSchema->idxHash.count
+ pSchema->fkeyHash.count
);
- nByte += sqlite3MallocSize(pSchema->tblHash.ht);
- nByte += sqlite3MallocSize(pSchema->trigHash.ht);
- nByte += sqlite3MallocSize(pSchema->idxHash.ht);
- nByte += sqlite3MallocSize(pSchema->fkeyHash.ht);
+ nByte += sqlite3_msize(pSchema->tblHash.ht);
+ nByte += sqlite3_msize(pSchema->trigHash.ht);
+ nByte += sqlite3_msize(pSchema->idxHash.ht);
+ nByte += sqlite3_msize(pSchema->fkeyHash.ht);
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
diff --git a/src/test1.c b/src/test1.c
index 6c47754bf..dd190feeb 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -4689,6 +4689,34 @@ static int test_db_release_memory(
}
/*
+** Usage: sqlite3_db_cacheflush DB
+**
+** Attempt to flush any dirty pages to disk.
+*/
+static int test_db_cacheflush(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int rc;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_db_cacheflush(db);
+ if( rc ){
+ Tcl_SetResult(interp, (char *)sqlite3ErrStr(rc), TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+/*
** Usage: sqlite3_db_filename DB DBNAME
**
** Return the name of a file associated with a database.
@@ -6876,6 +6904,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_release_memory", test_release_memory, 0},
{ "sqlite3_db_release_memory", test_db_release_memory, 0},
+ { "sqlite3_db_cacheflush", test_db_cacheflush, 0},
{ "sqlite3_db_filename", test_db_filename, 0},
{ "sqlite3_db_readonly", test_db_readonly, 0},
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
diff --git a/src/test3.c b/src/test3.c
index 07d12d28c..212dff5fa 100644
--- a/src/test3.c
+++ b/src/test3.c
@@ -214,11 +214,12 @@ static int btree_cursor(
pBt = sqlite3TestTextToPtr(argv[1]);
if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
+ if( wrFlag ) wrFlag = BTREE_WRCSR;
pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
memset(pCur, 0, sqlite3BtreeCursorSize());
sqlite3BtreeEnter(pBt);
#ifndef SQLITE_OMIT_SHARED_CACHE
- rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
+ rc = sqlite3BtreeLockTable(pBt, iTable, !!wrFlag);
#endif
if( rc==SQLITE_OK ){
rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
@@ -598,6 +599,48 @@ static int btree_set_cache_size(
return TCL_OK;
}
+/*
+** usage: btree_insert CSR ?KEY? VALUE
+**
+** Set the size of the cache used by btree $ID.
+*/
+static int btree_insert(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const objv[]
+){
+ BtCursor *pCur;
+ int rc;
+ void *pKey = 0;
+ int nKey = 0;
+ void *pData = 0;
+ int nData = 0;
+
+ if( objc!=4 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE");
+ return TCL_ERROR;
+ }
+
+ if( objc==4 ){
+ if( Tcl_GetIntFromObj(interp, objv[2], &nKey) ) return TCL_ERROR;
+ pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &nData);
+ }else{
+ pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &nKey);
+ }
+ pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
+
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeInsert(pCur, pKey, nKey, pData, nData, 0, 0, 0);
+ sqlite3BtreeLeave(pCur->pBtree);
+
+ Tcl_ResetResult(interp);
+ if( rc ){
+ Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
/*
@@ -629,5 +672,7 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
}
+ Tcl_CreateObjCommand(interp, "btree_insert", btree_insert, 0, 0);
+
return TCL_OK;
}
diff --git a/src/test_config.c b/src/test_config.c
index ef59467a4..23db433c9 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -119,6 +119,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "8_3_names", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ Tcl_SetVar2(interp, "sqlite_options", "cursorhints", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "cursorhints", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_MEMSYS3
Tcl_SetVar2(interp, "sqlite_options", "mem3", "1", TCL_GLOBAL_ONLY);
#else
diff --git a/src/tokenize.c b/src/tokenize.c
index 6b5ad2790..9c1403bb2 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -467,7 +467,7 @@ abort_parse:
}
#ifdef YYTRACKMAXSTACKDEPTH
sqlite3_mutex_enter(sqlite3MallocMutex());
- sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK,
+ sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
sqlite3ParserStackPeak(pEngine)
);
sqlite3_mutex_leave(sqlite3MallocMutex());
diff --git a/src/update.c b/src/update.c
index d20098e9b..08b28ad7e 100644
--- a/src/update.c
+++ b/src/update.c
@@ -429,7 +429,7 @@ void sqlite3Update(
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
}
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen,
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
0, 0);
}
@@ -522,7 +522,7 @@ void sqlite3Update(
*/
testcase( i==31 );
testcase( i==32 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}
diff --git a/src/vdbe.c b/src/vdbe.c
index 9924e3b88..c1391427f 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -528,16 +528,24 @@ static int checkSavepointCount(sqlite3 *db){
/*
** Return the register of pOp->p2 after first preparing it to be
** overwritten with an integer value.
-*/
+*/
+static SQLITE_NOINLINE Mem *out2PrereleaseWithClear(Mem *pOut){
+ sqlite3VdbeMemSetNull(pOut);
+ pOut->flags = MEM_Int;
+ return pOut;
+}
static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
Mem *pOut;
assert( pOp->p2>0 );
assert( pOp->p2<=(p->nMem-p->nCursor) );
pOut = &p->aMem[pOp->p2];
memAboutToChange(p, pOut);
- if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
- pOut->flags = MEM_Int;
- return pOut;
+ if( VdbeMemDynamic(pOut) ){
+ return out2PrereleaseWithClear(pOut);
+ }else{
+ pOut->flags = MEM_Int;
+ return pOut;
+ }
}
@@ -1275,6 +1283,22 @@ case OP_SCopy: { /* out2 */
break;
}
+/* Opcode: IntCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
+**
+** Transfer the integer value held in register P1 into register P2.
+**
+** This is an optimized version of SCopy that works only for integer
+** values.
+*/
+case OP_IntCopy: { /* out2 */
+ pIn1 = &aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Int)!=0 );
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetInt64(pOut, pIn1->u.i);
+ break;
+}
+
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
@@ -2354,7 +2378,7 @@ case OP_Column: {
const u8 *zHdr; /* Next unparsed byte of the header */
const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset into the data */
- u32 szField; /* Number of bytes in the content of a field */
+ u64 offset64; /* 64-bit offset */
u32 avail; /* Number of bytes of available data */
u32 t; /* A type code from the record header */
u16 fx; /* pDest->flags value */
@@ -2425,19 +2449,6 @@ case OP_Column: {
pC->nHdrParsed = 0;
aOffset[0] = offset;
- /* Make sure a corrupt database has not given us an oversize header.
- ** Do this now to avoid an oversize memory allocation.
- **
- ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
- ** types use so much data space that there can only be 4096 and 32 of
- ** them, respectively. So the maximum header length results from a
- ** 3-byte type for each of the maximum of 32768 columns plus three
- ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
- */
- if( offset > 98307 || offset > pC->payloadSize ){
- rc = SQLITE_CORRUPT_BKPT;
- goto op_column_error;
- }
if( avail<offset ){
/* pC->aRow does not have to hold the entire row, but it does at least
@@ -2446,6 +2457,20 @@ case OP_Column: {
** dynamically allocated. */
pC->aRow = 0;
pC->szRow = 0;
+
+ /* Make sure a corrupt database has not given us an oversize header.
+ ** Do this now to avoid an oversize memory allocation.
+ **
+ ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
+ ** types use so much data space that there can only be 4096 and 32 of
+ ** them, respectively. So the maximum header length results from a
+ ** 3-byte type for each of the maximum of 32768 columns plus three
+ ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
+ */
+ if( offset > 98307 || offset > pC->payloadSize ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto op_column_error;
+ }
}
/* The following goto is an optimization. It can be omitted and
@@ -2468,11 +2493,8 @@ case OP_Column: {
/* Make sure zData points to enough of the record to cover the header. */
if( pC->aRow==0 ){
memset(&sMem, 0, sizeof(sMem));
- rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0],
- !pC->isTable, &sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_error;
- }
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], !pC->isTable, &sMem);
+ if( rc!=SQLITE_OK ) goto op_column_error;
zData = (u8*)sMem.z;
}else{
zData = pC->aRow;
@@ -2480,44 +2502,32 @@ case OP_Column: {
/* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
i = pC->nHdrParsed;
- offset = aOffset[i];
+ offset64 = aOffset[i];
zHdr = zData + pC->iHdrOffset;
zEndHdr = zData + aOffset[0];
assert( i<=p2 && zHdr<zEndHdr );
do{
- if( zHdr[0]<0x80 ){
- t = zHdr[0];
+ if( (t = zHdr[0])<0x80 ){
zHdr++;
+ offset64 += sqlite3VdbeOneByteSerialTypeLen(t);
}else{
zHdr += sqlite3GetVarint32(zHdr, &t);
+ offset64 += sqlite3VdbeSerialTypeLen(t);
}
- pC->aType[i] = t;
- szField = sqlite3VdbeSerialTypeLen(t);
- offset += szField;
- if( offset<szField ){ /* True if offset overflows */
- zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
- break;
- }
- i++;
- aOffset[i] = offset;
+ pC->aType[i++] = t;
+ aOffset[i] = (u32)(offset64 & 0xffffffff);
}while( i<=p2 && zHdr<zEndHdr );
pC->nHdrParsed = i;
pC->iHdrOffset = (u32)(zHdr - zData);
- if( pC->aRow==0 ){
- sqlite3VdbeMemRelease(&sMem);
- sMem.flags = MEM_Null;
- }
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
/* The record is corrupt if any of the following are true:
** (1) the bytes of the header extend past the declared header size
- ** (zHdr>zEndHdr)
** (2) the entire header was used but not all data was used
- ** (zHdr==zEndHdr && offset!=pC->payloadSize)
** (3) the end of the data extends beyond the end of the record.
- ** (offset > pC->payloadSize)
*/
- if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize))
- || (offset > pC->payloadSize)
+ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize))
+ || (offset64 > pC->payloadSize)
){
rc = SQLITE_CORRUPT_BKPT;
goto op_column_error;
@@ -2536,6 +2546,8 @@ case OP_Column: {
}
goto op_column_out;
}
+ }else{
+ t = pC->aType[p2];
}
/* Extract the content for the p2+1-th column. Control can only
@@ -2546,7 +2558,7 @@ case OP_Column: {
assert( rc==SQLITE_OK );
assert( sqlite3VdbeCheckMemInvariants(pDest) );
if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest);
- t = pC->aType[p2];
+ assert( t==pC->aType[p2] );
if( pC->szRow>=aOffset[p2+1] ){
/* This is the common case where the desired content fits on the original
** page - where the content is not on an overflow page */
@@ -2658,7 +2670,7 @@ case OP_MakeRecord: {
int file_format; /* File format to use for encoding */
int i; /* Space used in zNewRecord[] header */
int j; /* Space used in zNewRecord[] content */
- int len; /* Length of a field */
+ u32 len; /* Length of a field */
/* Assuming the record contains N fields, the record format looks
** like this:
@@ -2708,8 +2720,7 @@ case OP_MakeRecord: {
pRec = pLast;
do{
assert( memIsValid(pRec) );
- pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format);
- len = sqlite3VdbeSerialTypeLen(serial_type);
+ pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
if( pRec->flags & MEM_Zero ){
if( nData ){
if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
@@ -3323,7 +3334,6 @@ case OP_ReopenIdx: {
case OP_OpenRead:
case OP_OpenWrite:
- assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR|OPFLAG_SEEKEQ))==pOp->p5 );
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
assert( p->bIsReader );
assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
@@ -3344,7 +3354,8 @@ case OP_OpenWrite:
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
- wrFlag = 1;
+ assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
+ wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->pSchema->file_format;
@@ -3396,8 +3407,12 @@ case OP_OpenWrite:
open_cursor_set_hints:
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ );
- sqlite3BtreeCursorHints(pCur->pCursor,
- (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
+ testcase( pOp->p5 & OPFLAG_BULKCSR );
+#ifdef SQLITE_ENABLE_CURSOR_HINT
+ testcase( pOp->p2 & OPFLAG_SEEKEQ );
+#endif
+ sqlite3BtreeCursorHintFlags(pCur->pCursor,
+ (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
break;
}
@@ -3464,11 +3479,11 @@ case OP_OpenEphemeral: {
assert( pKeyInfo->db==db );
assert( pKeyInfo->enc==ENC(db) );
pCx->pKeyInfo = pKeyInfo;
- rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR, pKeyInfo, pCx->pCursor);
}
pCx->isTable = 0;
}else{
- rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
+ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR, 0, pCx->pCursor);
pCx->isTable = 1;
}
}
@@ -6637,6 +6652,26 @@ case OP_Init: { /* jump */
break;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/* Opcode: CursorHint P1 * * P4 *
+**
+** Provide a hint to cursor P1 that it only needs to return rows that
+** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer
+** to values currently held in registers. TK_COLUMN terms in the P4
+** expression refer to columns in the b-tree to which cursor P1 is pointing.
+*/
+case OP_CursorHint: {
+ VdbeCursor *pC;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_EXPR );
+ pC = p->apCsr[pOp->p1];
+ if( pC ){
+ sqlite3BtreeCursorHint(pC->pCursor, BTREE_HINT_RANGE, pOp->p4.pExpr, aMem);
+ }
+ break;
+}
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/* Opcode: Noop * * * * *
**
diff --git a/src/vdbe.h b/src/vdbe.h
index 0c9350d42..55c4f82cf 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -61,6 +61,9 @@ struct VdbeOp {
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
Table *pTab; /* Used when p4type is P4_TABLE */
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ Expr *pExpr; /* Used when p4type is P4_EXPR */
+#endif
int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
@@ -111,6 +114,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */
#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */
#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
+#define P4_EXPR (-7) /* P4 is a pointer to an Expr tree */
#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 7ff10dfb8..02e98e14f 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -27,6 +27,17 @@
#endif
/*
+** VDBE_DISPLAY_P4 is true or false depending on whether or not the
+** "explain" P4 display logic is enabled.
+*/
+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+# define VDBE_DISPLAY_P4 1
+#else
+# define VDBE_DISPLAY_P4 0
+#endif
+
+/*
** SQL is translated into a sequence of instructions to be
** executed by a virtual machine. Each instruction is an instance
** of the following structure.
@@ -431,7 +442,8 @@ int sqlite3VdbeCursorRestore(VdbeCursor*);
void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
u32 sqlite3VdbeSerialTypeLen(u32);
-u32 sqlite3VdbeSerialType(Mem*, int);
+u8 sqlite3VdbeOneByteSerialTypeLen(u8);
+u32 sqlite3VdbeSerialType(Mem*, int, u32*);
u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 91189bc33..750f5f64a 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -764,6 +764,12 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
break;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ sqlite3ExprDelete(db, (Expr*)p4);
+ break;
+ }
+#endif
case P4_MPRINTF: {
if( db->pnBytesFreed==0 ) sqlite3_free(p4);
break;
@@ -896,6 +902,15 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
}else if( n==P4_KEYINFO ){
pOp->p4.p = (void*)zP4;
pOp->p4type = P4_KEYINFO;
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ }else if( n==P4_EXPR ){
+ /* Responsibility for deleting the Expr tree is handed over to the
+ ** VDBE by this operation. The caller should have already invoked
+ ** sqlite3ExprDup() or whatever other routine is needed to make a
+ ** private copy of the tree. */
+ pOp->p4.pExpr = (Expr*)zP4;
+ pOp->p4type = P4_EXPR;
+#endif
}else if( n==P4_VTAB ){
pOp->p4.p = (void*)zP4;
pOp->p4type = P4_VTAB;
@@ -1086,9 +1101,84 @@ static int displayComment(
}
#endif /* SQLITE_DEBUG */
+#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
+/*
+** Translate the P4.pExpr value for an OP_CursorHint opcode into text
+** that can be displayed in the P4 column of EXPLAIN output.
+*/
+static int displayP4Expr(int nTemp, char *zTemp, Expr *pExpr){
+ const char *zOp = 0;
+ int n;
+ switch( pExpr->op ){
+ case TK_STRING:
+ sqlite3_snprintf(nTemp, zTemp, "%Q", pExpr->u.zToken);
+ break;
+ case TK_INTEGER:
+ sqlite3_snprintf(nTemp, zTemp, "%d", pExpr->u.iValue);
+ break;
+ case TK_NULL:
+ sqlite3_snprintf(nTemp, zTemp, "NULL");
+ break;
+ case TK_REGISTER: {
+ sqlite3_snprintf(nTemp, zTemp, "r[%d]", pExpr->iTable);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iColumn<0 ){
+ sqlite3_snprintf(nTemp, zTemp, "rowid");
+ }else{
+ sqlite3_snprintf(nTemp, zTemp, "c%d", (int)pExpr->iColumn);
+ }
+ break;
+ }
+ case TK_LT: zOp = "LT"; break;
+ case TK_LE: zOp = "LE"; break;
+ case TK_GT: zOp = "GT"; break;
+ case TK_GE: zOp = "GE"; break;
+ case TK_NE: zOp = "NE"; break;
+ case TK_EQ: zOp = "EQ"; break;
+ case TK_IS: zOp = "IS"; break;
+ case TK_ISNOT: zOp = "ISNOT"; break;
+ case TK_AND: zOp = "AND"; break;
+ case TK_OR: zOp = "OR"; break;
+ case TK_PLUS: zOp = "ADD"; break;
+ case TK_STAR: zOp = "MUL"; break;
+ case TK_MINUS: zOp = "SUB"; break;
+ case TK_REM: zOp = "REM"; break;
+ case TK_BITAND: zOp = "BITAND"; break;
+ case TK_BITOR: zOp = "BITOR"; break;
+ case TK_SLASH: zOp = "DIV"; break;
+ case TK_LSHIFT: zOp = "LSHIFT"; break;
+ case TK_RSHIFT: zOp = "RSHIFT"; break;
+ case TK_CONCAT: zOp = "CONCAT"; break;
+ case TK_UMINUS: zOp = "MINUS"; break;
+ case TK_UPLUS: zOp = "PLUS"; break;
+ case TK_BITNOT: zOp = "BITNOT"; break;
+ case TK_NOT: zOp = "NOT"; break;
+ case TK_ISNULL: zOp = "ISNULL"; break;
+ case TK_NOTNULL: zOp = "NOTNULL"; break;
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+ default:
+ sqlite3_snprintf(nTemp, zTemp, "%s", "expr");
+ break;
+ }
+
+ if( zOp ){
+ sqlite3_snprintf(nTemp, zTemp, "%s(", zOp);
+ n = sqlite3Strlen30(zTemp);
+ n += displayP4Expr(nTemp-n, zTemp+n, pExpr->pLeft);
+ if( n<nTemp-1 && pExpr->pRight ){
+ zTemp[n++] = ',';
+ n += displayP4Expr(nTemp-n, zTemp+n, pExpr->pRight);
+ }
+ sqlite3_snprintf(nTemp-n, zTemp+n, ")");
+ }
+ return sqlite3Strlen30(zTemp);
+}
+#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
+
+
+#if VDBE_DISPLAY_P4
/*
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
@@ -1128,6 +1218,12 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
assert( i<nTemp );
break;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ displayP4Expr(nTemp, zTemp, pOp->p4.pExpr);
+ break;
+ }
+#endif
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName);
@@ -1203,7 +1299,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
assert( zP4!=0 );
return zP4;
}
-#endif
+#endif /* VDBE_DISPLAY_P4 */
/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
@@ -1517,12 +1613,12 @@ int sqlite3VdbeList(
pMem->u.i = pOp->p3; /* P3 */
pMem++;
- if( sqlite3VdbeMemClearAndResize(pMem, 32) ){ /* P4 */
+ if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
- zP4 = displayP4(pOp, pMem->z, 32);
+ zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
if( zP4!=pMem->z ){
sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
@@ -2941,11 +3037,13 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
/*
** Return the serial-type for the value stored in pMem.
*/
-u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
+u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
u32 n;
+ assert( pLen!=0 );
if( flags&MEM_Null ){
+ *pLen = 0;
return 0;
}
if( flags&MEM_Int ){
@@ -2959,15 +3057,23 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
u = i;
}
if( u<=127 ){
- return ((i&1)==i && file_format>=4) ? 8+(u32)u : 1;
+ if( (i&1)==i && file_format>=4 ){
+ *pLen = 0;
+ return 8+(u32)u;
+ }else{
+ *pLen = 1;
+ return 1;
+ }
}
- if( u<=32767 ) return 2;
- if( u<=8388607 ) return 3;
- if( u<=2147483647 ) return 4;
- if( u<=MAX_6BYTE ) return 5;
+ if( u<=32767 ){ *pLen = 2; return 2; }
+ if( u<=8388607 ){ *pLen = 3; return 3; }
+ if( u<=2147483647 ){ *pLen = 4; return 4; }
+ if( u<=MAX_6BYTE ){ *pLen = 6; return 5; }
+ *pLen = 8;
return 6;
}
if( flags&MEM_Real ){
+ *pLen = 8;
return 7;
}
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
@@ -2976,26 +3082,46 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
if( flags & MEM_Zero ){
n += pMem->u.nZero;
}
+ *pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
/*
-** The sizes for serial types less than 12
+** The sizes for serial types less than 128
*/
static const u8 sqlite3SmallTypeSizes[] = {
- 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0
+ /* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0,
+/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+/* 30 */ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
+/* 40 */ 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
+/* 50 */ 19, 19, 20, 20, 21, 21, 22, 22, 23, 23,
+/* 60 */ 24, 24, 25, 25, 26, 26, 27, 27, 28, 28,
+/* 70 */ 29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
+/* 80 */ 34, 34, 35, 35, 36, 36, 37, 37, 38, 38,
+/* 90 */ 39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
+/* 100 */ 44, 44, 45, 45, 46, 46, 47, 47, 48, 48,
+/* 110 */ 49, 49, 50, 50, 51, 51, 52, 52, 53, 53,
+/* 120 */ 54, 54, 55, 55, 56, 56, 57, 57
};
/*
** Return the length of the data corresponding to the supplied serial-type.
*/
u32 sqlite3VdbeSerialTypeLen(u32 serial_type){
- if( serial_type>=12 ){
+ if( serial_type>=128 ){
return (serial_type-12)/2;
}else{
+ assert( serial_type<12
+ || sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 );
return sqlite3SmallTypeSizes[serial_type];
}
}
+u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
+ assert( serial_type<128 );
+ return sqlite3SmallTypeSizes[serial_type];
+}
/*
** If we are on an architecture with mixed-endian floating
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 7f1a21978..b178662b9 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -1423,17 +1423,16 @@ static void recordFunc(
sqlite3_value **argv
){
const int file_format = 1;
- int iSerial; /* Serial type */
+ u32 iSerial; /* Serial type */
int nSerial; /* Bytes of space for iSerial as varint */
- int nVal; /* Bytes of space required for argv[0] */
+ u32 nVal; /* Bytes of space required for argv[0] */
int nRet;
sqlite3 *db;
u8 *aRet;
UNUSED_PARAMETER( argc );
- iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
nSerial = sqlite3VarintLen(iSerial);
- nVal = sqlite3VdbeSerialTypeLen(iSerial);
db = sqlite3_context_db_handle(context);
nRet = 1 + nSerial + nVal;
diff --git a/src/vdbesort.c b/src/vdbesort.c
index afc4d6abe..9840bed31 100644
--- a/src/vdbesort.c
+++ b/src/vdbesort.c
@@ -978,7 +978,7 @@ int sqlite3VdbeSorterInit(
}
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
pSorter->nTask = nWorker + 1;
- pSorter->iPrev = nWorker-1;
+ pSorter->iPrev = (u8)(nWorker - 1);
pSorter->bUseThreads = (pSorter->nTask>1);
pSorter->db = db;
for(i=0; i<pSorter->nTask; i++){
diff --git a/src/where.c b/src/where.c
index af8e2f35f..1c87706ea 100644
--- a/src/where.c
+++ b/src/where.c
@@ -485,14 +485,20 @@ static LogEst estLog(LogEst N){
** Convert OP_Column opcodes to OP_Copy in previously generated code.
**
** This routine runs over generated VDBE code and translates OP_Column
-** opcodes into OP_Copy, and OP_Rowid into OP_Null, when the table is being
-** accessed via co-routine instead of via table lookup.
+** opcodes into OP_Copy when the table is being accessed via co-routine
+** instead of via table lookup.
+**
+** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on
+** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero,
+** then each OP_Rowid is transformed into an instruction to increment the
+** value stored in its output register.
*/
static void translateColumnToCopy(
Vdbe *v, /* The VDBE containing code to translate */
int iStart, /* Translate from this opcode to the end */
int iTabCur, /* OP_Column/OP_Rowid references to this table */
- int iRegister /* The first column is in this register */
+ int iRegister, /* The first column is in this register */
+ int bIncrRowid /* If non-zero, transform OP_rowid to OP_AddImm(1) */
){
VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
int iEnd = sqlite3VdbeCurrentAddr(v);
@@ -504,9 +510,16 @@ static void translateColumnToCopy(
pOp->p2 = pOp->p3;
pOp->p3 = 0;
}else if( pOp->opcode==OP_Rowid ){
- pOp->opcode = OP_Null;
- pOp->p1 = 0;
- pOp->p3 = 0;
+ if( bIncrRowid ){
+ /* Increment the value stored in the P2 operand of the OP_Rowid. */
+ pOp->opcode = OP_AddImm;
+ pOp->p1 = pOp->p2;
+ pOp->p2 = 1;
+ }else{
+ pOp->opcode = OP_Null;
+ pOp->p1 = 0;
+ pOp->p3 = 0;
+ }
}
}
}
@@ -614,6 +627,8 @@ static void constructAutomaticIndex(
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ int addrCounter = 0; /* Address where integer counter is initialized */
+ int regBase; /* Array of registers where record is assembled */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -742,6 +757,7 @@ static void constructAutomaticIndex(
pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
if( pTabItem->fg.viaCoroutine ){
int regYield = pTabItem->regReturn;
+ addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
VdbeCoverage(v);
@@ -755,12 +771,15 @@ static void constructAutomaticIndex(
pLoop->wsFlags |= WHERE_PARTIALIDX;
}
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0);
+ regBase = sqlite3GenerateIndexKey(
+ pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
+ );
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
if( pTabItem->fg.viaCoroutine ){
- translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult);
+ sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
+ translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult, 1);
sqlite3VdbeGoto(v, addrTop);
pTabItem->fg.viaCoroutine = 0;
}else{
@@ -3991,6 +4010,7 @@ WhereInfo *sqlite3WhereBegin(
int ii; /* Loop counter */
sqlite3 *db; /* Database connection */
int rc; /* Return code */
+ u8 bFordelete = 0;
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
(wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
@@ -4246,8 +4266,11 @@ WhereInfo *sqlite3WhereBegin(
&& 0==(wsFlags & WHERE_VIRTUALTABLE)
)){
pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
- if( HasRowid(pTabList->a[0].pTab) ){
- pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
+ if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
+ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){
+ bFordelete = OPFLAG_FORDELETE;
+ }
+ pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY);
}
}
}
@@ -4295,6 +4318,14 @@ WhereInfo *sqlite3WhereBegin(
SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ if( pLoop->u.btree.pIndex!=0 ){
+ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete);
+ }else
+#endif
+ {
+ sqlite3VdbeChangeP5(v, bFordelete);
+ }
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
(const u8*)&pTabItem->colUsed, P4_INT64);
@@ -4509,7 +4540,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
if( pTabItem->fg.viaCoroutine && !db->mallocFailed ){
translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur,
- pTabItem->regResult);
+ pTabItem->regResult, 0);
continue;
}
diff --git a/src/wherecode.c b/src/wherecode.c
index 910ff34e0..87db0e0a2 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -590,6 +590,154 @@ static void whereLikeOptimizationStringFixup(
}
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Information is passed from codeCursorHint() down to individual nodes of
+** the expression tree (by sqlite3WalkExpr()) using an instance of this
+** structure.
+*/
+struct CCurHint {
+ int iTabCur; /* Cursor for the main table */
+ int iIdxCur; /* Cursor for the index, if pIdx!=0. Unused otherwise */
+ Index *pIdx; /* The index used to access the table */
+};
+
+/*
+** This function is called for every node of an expression that is a candidate
+** for a cursor hint on an index cursor. For TK_COLUMN nodes that reference
+** the table CCurHint.iTabCur, verify that the same column can be
+** accessed through the index. If it cannot, then set pWalker->eCode to 1.
+*/
+static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ assert( pHint->pIdx!=0 );
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iTable==pHint->iTabCur
+ && sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn)<0
+ ){
+ pWalker->eCode = 1;
+ }
+ return WRC_Continue;
+}
+
+
+/*
+** This function is called on every node of an expression tree used as an
+** argument to the OP_CursorHint instruction. If the node is a TK_COLUMN
+** that accesses any table other than the one identified by
+** CCurHint.iTabCur, then do the following:
+**
+** 1) allocate a register and code an OP_Column instruction to read
+** the specified column into the new register, and
+**
+** 2) transform the expression node to a TK_REGISTER node that reads
+** from the newly populated register.
+**
+** Also, if the node is a TK_COLUMN that does access the table idenified
+** by pCCurHint.iTabCur, and an index is being used (which we will
+** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
+** an access of the index rather than the original table.
+*/
+static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
+ int rc = WRC_Continue;
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ if( pExpr->op==TK_COLUMN ){
+ if( pExpr->iTable!=pHint->iTabCur ){
+ Vdbe *v = pWalker->pParse->pVdbe;
+ int reg = ++pWalker->pParse->nMem; /* Register for column value */
+ sqlite3ExprCodeGetColumnOfTable(
+ v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
+ );
+ pExpr->op = TK_REGISTER;
+ pExpr->iTable = reg;
+ }else if( pHint->pIdx!=0 ){
+ pExpr->iTable = pHint->iIdxCur;
+ pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
+ assert( pExpr->iColumn>=0 );
+ }
+ }else if( pExpr->op==TK_AGG_FUNCTION ){
+ /* An aggregate function in the WHERE clause of a query means this must
+ ** be a correlated sub-query, and expression pExpr is an aggregate from
+ ** the parent context. Do not walk the function arguments in this case.
+ **
+ ** todo: It should be possible to replace this node with a TK_REGISTER
+ ** expression, as the result of the expression must be stored in a
+ ** register at this point. The same holds for TK_AGG_COLUMN nodes. */
+ rc = WRC_Prune;
+ }
+ return rc;
+}
+
+/*
+** Insert an OP_CursorHint instruction if it is appropriate to do so.
+*/
+static void codeCursorHint(
+ WhereInfo *pWInfo, /* The where clause */
+ WhereLevel *pLevel, /* Which loop to provide hints for */
+ WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
+){
+ Parse *pParse = pWInfo->pParse;
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+ Expr *pExpr = 0;
+ WhereLoop *pLoop = pLevel->pWLoop;
+ int iCur;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ int i, j;
+ struct CCurHint sHint;
+ Walker sWalker;
+
+ if( OptimizationDisabled(db, SQLITE_CursorHints) ) return;
+ iCur = pLevel->iTabCur;
+ assert( iCur==pWInfo->pTabList->a[pLevel->iFrom].iCursor );
+ sHint.iTabCur = iCur;
+ sHint.iIdxCur = pLevel->iIdxCur;
+ sHint.pIdx = pLoop->u.btree.pIndex;
+ memset(&sWalker, 0, sizeof(sWalker));
+ sWalker.pParse = pParse;
+ sWalker.u.pCCurHint = &sHint;
+ pWC = &pWInfo->sWC;
+ for(i=0; i<pWC->nTerm; i++){
+ pTerm = &pWC->a[i];
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( pTerm->prereqAll & pLevel->notReady ) continue;
+ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
+
+ /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
+ ** the cursor. These terms are not needed as hints for a pure range
+ ** scan (that has no == terms) so omit them. */
+ if( pLoop->u.btree.nEq==0 && pTerm!=pEndRange ){
+ for(j=0; j<pLoop->nLTerm && pLoop->aLTerm[j]!=pTerm; j++){}
+ if( j<pLoop->nLTerm ) continue;
+ }
+
+ /* No subqueries or non-deterministic functions allowed */
+ if( sqlite3ExprContainsSubquery(pTerm->pExpr) ) continue;
+
+ /* For an index scan, make sure referenced columns are actually in
+ ** the index. */
+ if( sHint.pIdx!=0 ){
+ sWalker.eCode = 0;
+ sWalker.xExprCallback = codeCursorHintCheckExpr;
+ sqlite3WalkExpr(&sWalker, pTerm->pExpr);
+ if( sWalker.eCode ) continue;
+ }
+
+ /* If we survive all prior tests, that means this term is worth hinting */
+ pExpr = sqlite3ExprAnd(db, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
+ }
+ if( pExpr!=0 ){
+ sWalker.xExprCallback = codeCursorHintFixExpr;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ sqlite3VdbeAddOp4(v, OP_CursorHint,
+ (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
+ (const char*)pExpr, P4_EXPR);
+ }
+}
+#else
+# define codeCursorHint(A,B,C) /* No-op */
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
@@ -754,6 +902,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pStart = pEnd;
pEnd = pTerm;
}
+ codeCursorHint(pWInfo, pLevel, pEnd);
if( pStart ){
Expr *pX; /* The expression that defines the start bound */
int r1, rTemp; /* Registers for holding the start boundary */
@@ -947,15 +1096,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
- /* Generate code to evaluate all constraint terms using == or IN
- ** and store the values of those terms in an array of registers
- ** starting at regBase.
- */
- regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
- assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
- if( zStartAff ) cEndAff = zStartAff[nEq];
- addrNxt = pLevel->addrNxt;
-
/* If we are doing a reverse order scan on an ascending index, or
** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd).
@@ -967,6 +1107,16 @@ Bitmask sqlite3WhereCodeOneLoopStart(
SWAP(u8, bSeekPastNull, bStopAtNull);
}
+ /* Generate code to evaluate all constraint terms using == or IN
+ ** and store the values of those terms in an array of registers
+ ** starting at regBase.
+ */
+ codeCursorHint(pWInfo, pLevel, pRangeEnd);
+ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
+ assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
+ if( zStartAff ) cEndAff = zStartAff[nEq];
+ addrNxt = pLevel->addrNxt;
+
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
@@ -1249,7 +1399,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
}
if( pAndExpr ){
- pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
+ pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr, 0);
}
}
@@ -1305,11 +1455,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
r = sqlite3GetTempRange(pParse, nPk);
for(iPk=0; iPk<nPk; iPk++){
int iCol = pPk->aiColumn[iPk];
- int rx;
- rx = sqlite3ExprCodeGetColumn(pParse, pTab, iCol, iCur,r+iPk,0);
- if( rx!=r+iPk ){
- sqlite3VdbeAddOp2(v, OP_SCopy, rx, r+iPk);
- }
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, iCol, iCur, r+iPk);
}
/* Check if the temp table already contains this key. If so,
@@ -1409,6 +1555,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** a pseudo-cursor. No need to Rewind or Next such cursors. */
pLevel->op = OP_Noop;
}else{
+ codeCursorHint(pWInfo, pLevel, 0);
pLevel->op = aStep[bRev];
pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);