aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2022-12-07 21:19:37 +0000
committerdrh <>2022-12-07 21:19:37 +0000
commit619ff3f505fe795d27cb48839f2b14fb6ce16091 (patch)
tree26a59289875bab592c14681085f33ef9bcd0127c /src
parentad9ff1d5f2db2c90271a07fc6e3c6a3d650094b3 (diff)
parent7f4b066eb2b5ab99ed126809e56e8a9bd41a91e3 (diff)
downloadsqlite-619ff3f505fe795d27cb48839f2b14fb6ce16091.tar.gz
sqlite-619ff3f505fe795d27cb48839f2b14fb6ce16091.zip
Merge the latest fixes and enhancements from trunk into the
coroutines-exp2 branch. FossilOrigin-Name: 1c5f41986f5af181bf389675361c9f176e9195e847319f07ebd5c87992ded38b
Diffstat (limited to 'src')
-rw-r--r--src/memdb.c28
-rw-r--r--src/shell.c.in92
-rw-r--r--src/test_demovfs.c1
-rw-r--r--src/vdbe.c118
-rw-r--r--src/vdbe.h4
-rw-r--r--src/vdbeInt.h6
-rw-r--r--src/vdbeapi.c28
-rw-r--r--src/vdbeaux.c30
-rw-r--r--src/where.c53
-rw-r--r--src/wherecode.c16
10 files changed, 237 insertions, 139 deletions
diff --git a/src/memdb.c b/src/memdb.c
index 7485f51ff..4ea3cffde 100644
--- a/src/memdb.c
+++ b/src/memdb.c
@@ -370,21 +370,33 @@ static int memdbLock(sqlite3_file *pFile, int eLock){
int rc = SQLITE_OK;
if( eLock==pThis->eLock ) return SQLITE_OK;
memdbEnter(p);
+ assert( p->nWrLock==0 || p->nWrLock==1 ); /* No more than 1 write lock */
if( eLock>SQLITE_LOCK_SHARED ){
assert( pThis->eLock>=SQLITE_LOCK_SHARED );
if( p->mFlags & SQLITE_DESERIALIZE_READONLY ){
rc = SQLITE_READONLY;
}else if( eLock==SQLITE_LOCK_EXCLUSIVE ){
- /* Taking an EXCLUSIVE lock. Fail if we only have SHARED and any
- ** other client has any kind of write-lock. Also fail if any other
- ** client is holding read-lock. */
- if( pThis->eLock<=SQLITE_LOCK_SHARED && p->nWrLock ){
- rc = SQLITE_BUSY;
- }else if( p->nRdLock>1 ){
+ /* We never go for an EXCLUSIVE lock unless we already hold SHARED or
+ ** higher */
+ assert( pThis->eLock>=SQLITE_LOCK_SHARED );
+ testcase( pThis->eLock==SQLITE_LOCK_SHARED );
+
+ /* Because we are holding SHARED or more, there must be at least
+ ** one read lock */
+ assert( p->nRdLock>0 );
+
+ /* The only way that there can be an existing write lock is if we
+ ** currently hold it. Otherwise, we would have never been able to
+ ** promote from NONE to SHARED. */
+ assert( p->nWrLock==0 || pThis->eLock>SQLITE_LOCK_SHARED );
+
+ if( p->nRdLock>1 ){
+ /* Cannot take EXCLUSIVE if somebody else is holding SHARED */
rc = SQLITE_BUSY;
+ }else{
+ p->nWrLock = 1;
}
- p->nWrLock = 1;
- }else if( pThis->eLock<=SQLITE_LOCK_SHARED ){
+ }else if( ALWAYS(pThis->eLock<=SQLITE_LOCK_SHARED) ){
/* Upgrading to RESERVED or PENDING from SHARED. Fail if any other
** client has a write-lock of any kind. */
if( p->nWrLock ){
diff --git a/src/shell.c.in b/src/shell.c.in
index 85e708551..e4b054f45 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -482,24 +482,26 @@ static char continuePrompt[PROMPT_LEN_MAX];
*/
#ifdef SQLITE_OMIT_DYNAPROMPT
# define CONTINUATION_PROMPT continuePrompt
-# define CONTINUE_PROMPT_RESET(p)
+# define CONTINUE_PROMPT_RESET
# define CONTINUE_PROMPT_AWAITS(p,s)
# define CONTINUE_PROMPT_AWAITC(p,c)
# define CONTINUE_PAREN_INCR(p,n)
-# define CONTINUE_PROMPT_STATE 0
-# define SCAN_TRACKER_REFTYPE void*
+# define CONTINUE_PROMPT_PSTATE 0
+typedef void *t_NoDynaPrompt;
+# define SCAN_TRACKER_REFTYPE t_NoDynaPrompt
#else
# define CONTINUATION_PROMPT dynamicContinuePrompt()
-# define CONTINUE_PROMPT_RESET(p) \
- if(p) (setLexemeOpen(p,0,0), trackParenLevel(p,0))
+# define CONTINUE_PROMPT_RESET \
+ do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0)
# define CONTINUE_PROMPT_AWAITS(p,s) \
if(p && stdin_is_interactive) setLexemeOpen(p, s, 0)
# define CONTINUE_PROMPT_AWAITC(p,c) \
if(p && stdin_is_interactive) setLexemeOpen(p, 0, c)
# define CONTINUE_PAREN_INCR(p,n) \
if(p && stdin_is_interactive) (trackParenLevel(p,n))
-# define SCAN_TRACKER_REFTYPE struct DynaPrompt *
-# define CONTINUE_PROMPT_STATE (pDynPrompt)
+# define CONTINUE_PROMPT_PSTATE (&dynPrompt)
+typedef struct DynaPrompt *t_DynaPromptRef;
+# define SCAN_TRACKER_REFTYPE t_DynaPromptRef
static struct DynaPrompt {
char dynamicPrompt[PROMPT_LEN_MAX];
@@ -507,7 +509,6 @@ static struct DynaPrompt {
int inParenLevel;
char *zScannerAwaits;
} dynPrompt = { {0}, {0}, 0, 0 };
-static struct DynaPrompt *pDynPrompt = &dynPrompt;
/* Record parenthesis nesting level change, or force level to 0. */
static void trackParenLevel(struct DynaPrompt *p, int ni){
@@ -536,17 +537,17 @@ static char *dynamicContinuePrompt(void){
if( dynPrompt.zScannerAwaits ){
int ncp = strlen(continuePrompt), ndp = strlen(dynPrompt.zScannerAwaits);
if( ndp > ncp-3 ) return continuePrompt;
- strncpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits, ndp);
+ strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
PROMPT_LEN_MAX-4);
}else{
if( dynPrompt.inParenLevel>9 ){
- strncpy(dynPrompt.dynamicPrompt, "(..", 3);
+ strncpy(dynPrompt.dynamicPrompt, "(..", 4);
}else if( dynPrompt.inParenLevel<0 ){
- strncpy(dynPrompt.dynamicPrompt, ")x!", 3);
+ strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
}else{
- strncpy(dynPrompt.dynamicPrompt, "(x.", 3);
+ strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
}
strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4);
@@ -10261,12 +10262,12 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( bSchema ){
- zSql = "SELECT lower(name) FROM sqlite_schema"
+ zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" UNION ALL SELECT 'sqlite_schema'"
" ORDER BY 1 collate nocase";
}else{
- zSql = "SELECT lower(name) FROM sqlite_schema"
+ zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" AND name NOT LIKE 'sqlite_%'"
" ORDER BY 1 collate nocase";
@@ -10327,6 +10328,58 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
shell_exec(p, zSql, 0);
}
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
+ {
+ int lrc;
+ char *zRevText = /* Query for reversible to-blob-to-text check */
+ "SELECT lower(name) as tname FROM sqlite_schema\n"
+ "WHERE type='table' AND coalesce(rootpage,0)>1\n"
+ "AND name NOT LIKE 'sqlite_%%'%s\n"
+ "ORDER BY 1 collate nocase";
+ zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
+ zRevText = sqlite3_mprintf(
+ /* lower-case query is first run, producing upper-case query. */
+ "with tabcols as materialized(\n"
+ "select tname, cname\n"
+ "from ("
+ " select ss.tname as tname, ti.name as cname\n"
+ " from (%z) ss\n inner join pragma_table_info(tname) ti))\n"
+ "select 'SELECT total(bad_text_count) AS bad_text_count\n"
+ "FROM ('||group_concat(query, ' UNION ALL ')||')' as btc_query\n"
+ " from (select 'SELECT COUNT(*) AS bad_text_count\n"
+ "FROM '||tname||' WHERE '\n"
+ "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
+ "|| ' AND typeof('||cname||')=''text'' ',\n"
+ "' OR ') as query, tname from tabcols group by tname)"
+ , zRevText);
+ shell_check_oom(zRevText);
+ if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
+ lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
+ assert(lrc==SQLITE_OK);
+ if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC);
+ lrc = SQLITE_ROW==sqlite3_step(pStmt);
+ if( lrc ){
+ const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
+ sqlite3_stmt *pCheckStmt;
+ lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
+ if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery);
+ if( SQLITE_OK==lrc ){
+ if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){
+ double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
+ if( countIrreversible>0 ){
+ int n = (int)(countIrreversible + 0.5);
+ utf8_printf(stderr,
+ "Digest includes %d invalidly encoded text field%s.\n",
+ n, (n>1)? "s": "");
+ }
+ }
+ sqlite3_finalize(pCheckStmt);
+ }
+ sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zRevText);
+ }
+#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
sqlite3_free(zSql);
}else
@@ -11118,6 +11171,7 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss,
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
+ /* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
@@ -11288,7 +11342,7 @@ static int process_input(ShellState *p){
}
++p->inputNesting;
p->lineno = 0;
- CONTINUE_PROMPT_RESET(CONTINUE_PROMPT_STATE);
+ CONTINUE_PROMPT_RESET;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
zLine = one_input_line(p->in, zLine, nSql>0);
@@ -11307,7 +11361,7 @@ static int process_input(ShellState *p){
&& line_is_complete(zSql, nSql) ){
memcpy(zLine,";",2);
}
- qss = quickscan(zLine, qss, CONTINUE_PROMPT_STATE);
+ qss = quickscan(zLine, qss, CONTINUE_PROMPT_PSTATE);
if( QSS_PLAINWHITE(qss) && nSql==0 ){
/* Just swallow single-line whitespace */
echo_group_input(p, zLine);
@@ -11315,7 +11369,7 @@ static int process_input(ShellState *p){
continue;
}
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
- CONTINUE_PROMPT_RESET(CONTINUE_PROMPT_STATE);
+ CONTINUE_PROMPT_RESET;
echo_group_input(p, zLine);
if( zLine[0]=='.' ){
rc = do_meta_command(zLine, p);
@@ -11351,7 +11405,7 @@ static int process_input(ShellState *p){
if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
echo_group_input(p, zSql);
errCnt += runOneSqlLine(p, zSql, p->in, startline);
- CONTINUE_PROMPT_RESET(CONTINUE_PROMPT_STATE);
+ CONTINUE_PROMPT_RESET;
nSql = 0;
if( p->outCount ){
output_reset(p);
@@ -11371,7 +11425,7 @@ static int process_input(ShellState *p){
/* This may be incomplete. Let the SQL parser deal with that. */
echo_group_input(p, zSql);
errCnt += runOneSqlLine(p, zSql, p->in, startline);
- CONTINUE_PROMPT_RESET(CONTINUE_PROMPT_STATE);
+ CONTINUE_PROMPT_RESET;
}
free(zSql);
free(zLine);
diff --git a/src/test_demovfs.c b/src/test_demovfs.c
index 29307675b..e990e98f2 100644
--- a/src/test_demovfs.c
+++ b/src/test_demovfs.c
@@ -461,7 +461,6 @@ static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
if( rc==0 && dirSync ){
int dfd; /* File descriptor open on directory */
- int i; /* Iterator variable */
char *zSlash;
char zDir[MAXPATHNAME+1]; /* Name of directory containing file zPath */
diff --git a/src/vdbe.c b/src/vdbe.c
index 1bf0ceb29..12b22992b 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -790,15 +790,12 @@ int sqlite3VdbeExec(
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
nVmStep++;
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- if( p->anExec ){
- assert( p->anExec && p->anCycle );
- p->anExec[(int)(pOp-aOp)]++;
- pnCycle = &p->anCycle[pOp-aOp];
+ pOp->nExec++;
+ pnCycle = &pOp->nCycle;
# ifdef VDBE_PROFILE
- if( sqlite3NProfileCnt==0 )
+ if( sqlite3NProfileCnt==0 )
# endif
- *pnCycle -= sqlite3Hwtime();
- }
+ *pnCycle -= sqlite3Hwtime();
#endif
/* Only allow tracing if SQLITE_DEBUG is defined.
@@ -2780,7 +2777,7 @@ case OP_Offset: { /* out3 */
** typeof() function or the IS NULL or IS NOT NULL operators or the
** equivalent. In this case, all content loading can be omitted.
*/
-case OP_Column: {
+case OP_Column: { /* ncycle */
u32 p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */
@@ -4132,7 +4129,7 @@ case OP_SetCookie: {
**
** See also: OP_OpenRead, OP_ReopenIdx
*/
-case OP_ReopenIdx: {
+case OP_ReopenIdx: { /* ncycle */
int nField;
KeyInfo *pKeyInfo;
u32 p2;
@@ -4153,7 +4150,7 @@ case OP_ReopenIdx: {
}
/* If the cursor is not currently open or is open on a different
** index, then fall through into OP_OpenRead to force a reopen */
-case OP_OpenRead:
+case OP_OpenRead: /* ncycle */
case OP_OpenWrite:
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
@@ -4247,7 +4244,7 @@ open_cursor_set_hints:
**
** Duplicate ephemeral cursors are used for self-joins of materialized views.
*/
-case OP_OpenDup: {
+case OP_OpenDup: { /* ncycle */
VdbeCursor *pOrig; /* The original cursor to be duplicated */
VdbeCursor *pCx; /* The new cursor */
@@ -4309,8 +4306,8 @@ case OP_OpenDup: {
** by this opcode will be used for automatically created transient
** indices in joins.
*/
-case OP_OpenAutoindex:
-case OP_OpenEphemeral: {
+case OP_OpenAutoindex: /* ncycle */
+case OP_OpenEphemeral: { /* ncycle */
VdbeCursor *pCx;
KeyInfo *pKeyInfo;
@@ -4468,7 +4465,7 @@ case OP_OpenPseudo: {
** Close a cursor previously opened as P1. If P1 is not
** currently open, this instruction is a no-op.
*/
-case OP_Close: {
+case OP_Close: { /* ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]);
p->apCsr[pOp->p1] = 0;
@@ -4585,10 +4582,10 @@ case OP_ColumnsUsed: {
**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLT: /* jump, in3, group */
-case OP_SeekLE: /* jump, in3, group */
-case OP_SeekGE: /* jump, in3, group */
-case OP_SeekGT: { /* jump, in3, group */
+case OP_SeekLT: /* jump, in3, group, ncycle */
+case OP_SeekLE: /* jump, in3, group, ncycle */
+case OP_SeekGE: /* jump, in3, group, ncycle */
+case OP_SeekGT: { /* jump, in3, group, ncycle */
int res; /* Comparison result */
int oc; /* Opcode */
VdbeCursor *pC; /* The cursor to seek */
@@ -4854,7 +4851,7 @@ seek_not_found:
** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0.
** </ol>
*/
-case OP_SeekScan: {
+case OP_SeekScan: { /* ncycle */
VdbeCursor *pC;
int res;
int nStep;
@@ -4976,7 +4973,7 @@ case OP_SeekScan: {
**
** P1 must be a valid b-tree cursor.
*/
-case OP_SeekHit: {
+case OP_SeekHit: { /* ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -5108,7 +5105,7 @@ case OP_IfNotOpen: { /* jump */
**
** See also: NotFound, Found, NotExists
*/
-case OP_IfNoHope: { /* jump, in3 */
+case OP_IfNoHope: { /* jump, in3, ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -5122,9 +5119,9 @@ case OP_IfNoHope: { /* jump, in3 */
/* Fall through into OP_NotFound */
/* no break */ deliberate_fall_through
}
-case OP_NoConflict: /* jump, in3 */
-case OP_NotFound: /* jump, in3 */
-case OP_Found: { /* jump, in3 */
+case OP_NoConflict: /* jump, in3, ncycle */
+case OP_NotFound: /* jump, in3, ncycle */
+case OP_Found: { /* jump, in3, ncycle */
int alreadyExists;
int ii;
VdbeCursor *pC;
@@ -5254,7 +5251,7 @@ case OP_Found: { /* jump, in3 */
**
** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_SeekRowid: { /* jump, in3 */
+case OP_SeekRowid: { /* jump, in3, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -5279,7 +5276,7 @@ case OP_SeekRowid: { /* jump, in3 */
}
/* Fall through into OP_NotExists */
/* no break */ deliberate_fall_through
-case OP_NotExists: /* jump, in3 */
+case OP_NotExists: /* jump, in3, ncycle */
pIn3 = &aMem[pOp->p3];
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
@@ -5903,7 +5900,7 @@ case OP_RowData: {
** be a separate OP_VRowid opcode for use with virtual tables, but this
** one opcode now works for both table types.
*/
-case OP_Rowid: { /* out2 */
+case OP_Rowid: { /* out2, ncycle */
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
@@ -6002,8 +5999,8 @@ case OP_NullRow: {
** from the end toward the beginning. In other words, the cursor is
** configured to use Prev, not Next.
*/
-case OP_SeekEnd:
-case OP_Last: { /* jump */
+case OP_SeekEnd: /* ncycle */
+case OP_Last: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -6108,7 +6105,7 @@ case OP_Sort: { /* jump */
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
*/
-case OP_Rewind: { /* jump */
+case OP_Rewind: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -6202,7 +6199,7 @@ case OP_SorterNext: { /* jump */
rc = sqlite3VdbeSorterNext(db, pC);
goto next_tail;
-case OP_Prev: /* jump */
+case OP_Prev: /* jump, ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p5==0
|| pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
@@ -6217,7 +6214,7 @@ case OP_Prev: /* jump */
rc = sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3);
goto next_tail;
-case OP_Next: /* jump */
+case OP_Next: /* jump, ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p5==0
|| pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
@@ -6409,8 +6406,8 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
-case OP_DeferredSeek:
-case OP_IdxRowid: { /* out2 */
+case OP_DeferredSeek: /* ncycle */
+case OP_IdxRowid: { /* out2, ncycle */
VdbeCursor *pC; /* The P1 index cursor */
VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */
i64 rowid; /* Rowid that P1 current points to */
@@ -6472,8 +6469,8 @@ case OP_IdxRowid: { /* out2 */
** seek operation now, without further delay. If the cursor seek has
** already occurred, this instruction is a no-op.
*/
-case OP_FinishSeek: {
- VdbeCursor *pC; /* The P1 index cursor */
+case OP_FinishSeek: { /* ncycle */
+ VdbeCursor *pC; /* The P1 index cursor */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -6528,10 +6525,10 @@ case OP_FinishSeek: {
** If the P1 index entry is less than or equal to the key value then jump
** to P2. Otherwise fall through to the next instruction.
*/
-case OP_IdxLE: /* jump */
-case OP_IdxGT: /* jump */
-case OP_IdxLT: /* jump */
-case OP_IdxGE: { /* jump */
+case OP_IdxLE: /* jump, ncycle */
+case OP_IdxGT: /* jump, ncycle */
+case OP_IdxLT: /* jump, ncycle */
+case OP_IdxGE: { /* jump, ncycle */
VdbeCursor *pC;
int res;
UnpackedRecord r;
@@ -7152,10 +7149,6 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- pFrame->anExec = p->anExec;
- pFrame->anCycle = p->anCycle;
-#endif
#ifdef SQLITE_DEBUG
pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
#endif
@@ -7192,10 +7185,6 @@ case OP_Program: { /* jump */
memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8);
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- p->anExec = 0;
- p->anCycle = 0;
-#endif
#ifdef SQLITE_DEBUG
/* Verify that second and subsequent executions of the same trigger do not
** try to reuse register values from the first use. */
@@ -7952,7 +7941,7 @@ case OP_VDestroy: {
** P1 is a cursor number. This opcode opens a cursor to the virtual
** table and stores that cursor in P1.
*/
-case OP_VOpen: {
+case OP_VOpen: { /* ncycle */
VdbeCursor *pCur;
sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
@@ -7999,7 +7988,7 @@ case OP_VOpen: {
** cursor. Register P3 is used to hold the values returned by
** sqlite3_vtab_in_first() and sqlite3_vtab_in_next().
*/
-case OP_VInitIn: { /* out2 */
+case OP_VInitIn: { /* out2, ncycle */
VdbeCursor *pC; /* The cursor containing the RHS values */
ValueList *pRhs; /* New ValueList object to put in reg[P2] */
@@ -8036,7 +8025,7 @@ case OP_VInitIn: { /* out2 */
**
** A jump is made to P2 if the result set after filtering would be empty.
*/
-case OP_VFilter: { /* jump */
+case OP_VFilter: { /* jump, ncycle */
int nArg;
int iQuery;
const sqlite3_module *pModule;
@@ -8096,7 +8085,7 @@ case OP_VFilter: { /* jump */
** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are
** unused by OP_VColumn.
*/
-case OP_VColumn: {
+case OP_VColumn: { /* ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
@@ -8148,7 +8137,7 @@ case OP_VColumn: {
** jump to instruction P2. Or, if the virtual table has reached
** the end of its result set, then fall through to the next instruction.
*/
-case OP_VNext: { /* jump */
+case OP_VNext: { /* jump, ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
@@ -8732,17 +8721,11 @@ default: { /* This is really OP_Noop, OP_Explain */
}
#if defined(VDBE_PROFILE)
- assert( pnCycle );
- if( pnCycle ){
- *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
- assert( *pnCycle < (((u64)1) << 48) );
- pnCycle = 0;
- }
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
- if( pnCycle ){
- *pnCycle += sqlite3Hwtime();
- pnCycle = 0;
- }
+ *pnCycle += sqlite3Hwtime();
+ pnCycle = 0;
#endif
/* The following code adds nothing to the actual functionality
@@ -8819,11 +8802,10 @@ abort_due_to_error:
** top. */
vdbe_return:
#if defined(VDBE_PROFILE)
- if( pnCycle ){
- *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
- assert( *pnCycle < (((u64)1) << 48) );
- pnCycle = 0;
- }
+ if( pnCycle ){
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
+ }
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pnCycle ){
*pnCycle += sqlite3Hwtime();
diff --git a/src/vdbe.h b/src/vdbe.h
index 467c99934..17a301bd8 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -71,6 +71,10 @@ struct VdbeOp {
u32 iSrcLine; /* Source-code line that generated this opcode
** with flags in the upper 8 bits */
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ u64 nExec;
+ u64 nCycle;
+#endif
};
typedef struct VdbeOp VdbeOp;
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index df47e0544..cd112e9c4 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -171,8 +171,6 @@ struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */
- i64 *anExec; /* Event counters from parent frame */
- u64 *anCycle; /* Cycle counters from parent frame */
Mem *aMem; /* Array of memory cells for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u8 *aOnce; /* Bitmask used by OP_Once */
@@ -492,10 +490,6 @@ struct Vdbe {
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
AuxData *pAuxData; /* Linked list of auxdata allocations */
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- i64 *anExec; /* Number of times each op has been executed */
- u64 *anCycle; /* Sum of sqlite3HwTime() spent in each opcode */
-#endif
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 0644cffa5..414745e8b 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -15,6 +15,7 @@
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
+#include "opcodes.h"
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -2122,14 +2123,12 @@ int sqlite3_stmt_scanstatus_v2(
ScanStatus *pScan;
int idx;
- /* If the v2 flag is clear, then this function must ignore any ScanStatus
- ** structures with ScanStatus.addrLoop set to 0. */
if( iScan<0 ){
int ii;
if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){
i64 res = 0;
for(ii=0; ii<p->nOp; ii++){
- res += p->anCycle[ii];
+ res += p->aOp[ii].nCycle;
}
*(i64*)pOut = res;
return 0;
@@ -2140,6 +2139,8 @@ int sqlite3_stmt_scanstatus_v2(
idx = iScan;
pScan = &p->aScan[idx];
}else{
+ /* If the COMPLEX flag is clear, then this function must ignore any
+ ** ScanStatus structures with ScanStatus.addrLoop set to 0. */
for(idx=0; idx<p->nScan; idx++){
pScan = &p->aScan[idx];
if( pScan->zName ){
@@ -2153,7 +2154,7 @@ int sqlite3_stmt_scanstatus_v2(
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
if( pScan->addrLoop>0 ){
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop];
+ *(sqlite3_int64*)pOut = p->aOp[pScan->addrLoop].nExec;
}else{
*(sqlite3_int64*)pOut = -1;
}
@@ -2161,7 +2162,7 @@ int sqlite3_stmt_scanstatus_v2(
}
case SQLITE_SCANSTAT_NVISIT: {
if( pScan->addrVisit>0 ){
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit];
+ *(sqlite3_int64*)pOut = p->aOp[pScan->addrVisit].nExec;
}else{
*(sqlite3_int64*)pOut = -1;
}
@@ -2217,7 +2218,7 @@ int sqlite3_stmt_scanstatus_v2(
if( iIns==0 ) break;
if( iIns>0 ){
while( iIns<=iEnd ){
- res += p->anCycle[iIns];
+ res += p->aOp[iIns].nCycle;
iIns++;
}
}else{
@@ -2225,13 +2226,10 @@ int sqlite3_stmt_scanstatus_v2(
for(iOp=0; iOp<p->nOp; iOp++){
Op *pOp = &p->aOp[iOp];
if( pOp->p1!=iEnd ) continue;
- if( pOp->opcode!=OP_VFilter && pOp->opcode!=OP_VColumn
- && pOp->opcode!=OP_Rowid && pOp->opcode!=OP_VOpen
- && pOp->opcode!=OP_VNext
- ){
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){
continue;
}
- res += p->anCycle[iOp];
+ res += p->aOp[iOp].nCycle;
}
}
}
@@ -2263,7 +2261,11 @@ int sqlite3_stmt_scanstatus(
*/
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
- memset(p->anExec, 0, p->nOp * sizeof(i64));
- memset(p->anCycle, 0, p->nOp * sizeof(u64));
+ int ii;
+ for(ii=0; ii<p->nOp; ii++){
+ Op *pOp = &p->aOp[ii];
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+ }
}
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index e22b74119..8d284746f 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -262,6 +262,10 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+#endif
#ifdef SQLITE_DEBUG
if( p->db->flags & SQLITE_VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
@@ -2508,8 +2512,10 @@ void sqlite3VdbeRewind(Vdbe *p){
p->iStatement = 0;
p->nFkConstraint = 0;
#ifdef VDBE_PROFILE
- memset(p->anExec, 0, sizeof(i64)*p->nOp);
- memset(p->anCycle, 0, sizeof(u64)*p->nOp);
+ for(i=0; i<p->nOp; i++){
+ p->aOp[i].nExec = 0;
+ p->aOp[i].nCycle = 0;
+ }
#endif
}
@@ -2617,10 +2623,6 @@ void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*));
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64));
- p->anCycle = allocSpace(&x, 0, p->nOp*sizeof(u64));
-#endif
if( x.nNeeded ){
x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
x.nFree = x.nNeeded;
@@ -2629,10 +2631,6 @@ void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
- p->anCycle = allocSpace(&x, p->anCycle, p->nOp*sizeof(u64));
-#endif
}
}
@@ -2647,10 +2645,6 @@ void sqlite3VdbeMakeReady(
p->nMem = nMem;
initMemArray(p->aMem, nMem, db, MEM_Undefined);
memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*));
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- memset(p->anExec, 0, p->nOp*sizeof(i64));
- memset(p->anCycle, 0, p->nOp*sizeof(u64));
-#endif
}
sqlite3VdbeRewind(p);
}
@@ -2708,10 +2702,6 @@ static void closeCursorsInFrame(Vdbe *p){
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
closeCursorsInFrame(v);
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
- v->anExec = pFrame->anExec;
- v->anCycle = pFrame->anCycle;
-#endif
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -3543,8 +3533,8 @@ int sqlite3VdbeReset(Vdbe *p){
}
for(i=0; i<p->nOp; i++){
char zHdr[100];
- i64 cnt = p->anExec[i];
- i64 cycles = p->anCycle[i];
+ i64 cnt = p->aOp[i].nExec;
+ i64 cycles = p->aOp[i].nCycle;
sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
cnt,
cycles,
diff --git a/src/where.c b/src/where.c
index a13379947..25ad18b28 100644
--- a/src/where.c
+++ b/src/where.c
@@ -67,7 +67,7 @@ int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
** block sorting is required.
*/
int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
- return pWInfo->nOBSat;
+ return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat;
}
/*
@@ -812,6 +812,51 @@ static int termCanDriveIndex(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+/*
+** Argument pIdx represents an automatic index that the current statement
+** will create and populate. Add an OP_Explain with text of the form:
+**
+** CREATE AUTOMATIC INDEX ON <table>(<cols>) [WHERE <expr>]
+**
+** This is only required if sqlite3_stmt_scanstatus() is enabled, to
+** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP
+** values with. In order to avoid breaking legacy code and test cases,
+** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command.
+*/
+static void explainAutomaticIndex(
+ Parse *pParse,
+ Index *pIdx, /* Automatic index to explain */
+ int bPartial, /* True if pIdx is a partial index */
+ int *pAddrExplain /* OUT: Address of OP_Explain */
+){
+ if( pParse->explain!=2 ){
+ Table *pTab = pIdx->pTable;
+ const char *zSep = "";
+ char *zText = 0;
+ int ii = 0;
+ zText = sqlite3_mprintf("CREATE AUTOMATIC INDEX ON %s(", pTab->zName);
+ assert( pIdx->nColumn>1 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID );
+ for(ii=0; ii<(pIdx->nColumn-1); ii++){
+ const char *zName = 0;
+ int iCol = pIdx->aiColumn[ii];
+
+ zName = pTab->aCol[iCol].zCnName;
+ zText = sqlite3_mprintf("%z%s%s", zText, zSep, zName);
+ zSep = ", ";
+ }
+ *pAddrExplain = sqlite3VdbeExplain(
+ pParse, 0, "%s)%s", zText, (bPartial ? " WHERE <expr>" : "")
+ );
+ sqlite3_free(zText);
+ }
+}
+#else
+# define explainAutomaticIndex(a,b,c,d)
+#endif
+
/*
** Generate code to construct the Index object for an automatic index
** and to set up the WhereLevel object pLevel so that the code generator
@@ -847,6 +892,9 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
SrcItem *pTabItem; /* FROM clause term being indexed */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExp = 0; /* Address of OP_Explain */
+#endif
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -970,6 +1018,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
pIdx->azColl[n] = sqlite3StrBINARY;
/* Create the automatic index */
+ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp);
assert( pLevel->iIdxCur>=0 );
pLevel->iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
@@ -1005,6 +1054,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0,
regBase, pLoop->u.btree.nEq);
}
+ sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
@@ -1025,6 +1075,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
/* Jump here when skipping the initialization */
sqlite3VdbeJumpHere(v, addrInit);
+ sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1);
end_auto_index_create:
sqlite3ExprDelete(pParse->db, pPartial);
diff --git a/src/wherecode.c b/src/wherecode.c
index 54c79baf9..4c22e5dd6 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -294,16 +294,26 @@ void sqlite3WhereAddScanStatus(
){
const char *zObj = 0;
WhereLoop *pLoop = pLvl->pWLoop;
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
+ int wsFlags = pLoop->wsFlags;
+ int viaCoroutine = 0;
+
+ if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
zObj = pLoop->u.btree.pIndex->zName;
}else{
zObj = pSrclist->a[pLvl->iFrom].zName;
+ viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine;
}
sqlite3VdbeScanStatus(
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
);
- if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
- sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
+
+ if( viaCoroutine==0 ){
+ if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
+ }
+ if( wsFlags & WHERE_INDEXED ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
+ }
}
}
#endif