aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c1
-rw-r--r--src/expr.c298
-rw-r--r--src/global.c2
-rw-r--r--src/insert.c1
-rw-r--r--src/loadext.c5
-rw-r--r--src/main.c33
-rw-r--r--src/mem1.c89
-rw-r--r--src/os.h30
-rw-r--r--src/os_unix.c10
-rw-r--r--src/os_win.c89
-rw-r--r--src/parse.y3
-rw-r--r--src/printf.c6
-rw-r--r--src/resolve.c14
-rw-r--r--src/select.c179
-rw-r--r--src/shell.c26
-rw-r--r--src/sqlite.h.in15
-rw-r--r--src/sqliteInt.h37
-rw-r--r--src/test1.c34
-rw-r--r--src/test_multiplex.c215
-rw-r--r--src/test_osinst.c6
-rw-r--r--src/test_vfstrace.c8
-rw-r--r--src/trigger.c1
-rw-r--r--src/update.c11
-rw-r--r--src/vdbe.c64
-rw-r--r--src/vdbe.h1
-rw-r--r--src/vdbeInt.h23
-rw-r--r--src/vdbeaux.c24
-rw-r--r--src/vdbetrace.c117
-rw-r--r--src/wal.c41
-rw-r--r--src/where.c4
30 files changed, 1022 insertions, 365 deletions
diff --git a/src/analyze.c b/src/analyze.c
index b6a987ab8..cbfdc8587 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -529,6 +529,7 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
(char*)&stat3InitFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 2);
diff --git a/src/expr.c b/src/expr.c
index d50617377..22643ff33 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -870,7 +870,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
- pItem->iCol = pOldItem->iCol;
+ pItem->iOrderByCol = pOldItem->iOrderByCol;
pItem->iAlias = pOldItem->iAlias;
}
return pNew;
@@ -940,7 +940,7 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
return pNew;
}
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
- Select *pNew;
+ Select *pNew, *pPrior;
if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
@@ -951,7 +951,9 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
pNew->op = p->op;
- pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ if( pPrior ) pPrior->pNext = pNew;
+ pNew->pNext = 0;
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
pNew->iLimit = 0;
@@ -1374,6 +1376,15 @@ static int isCandidateForInOpt(Select *p){
#endif /* SQLITE_OMIT_SUBQUERY */
/*
+** Code an OP_Once instruction and allocate space for its flag. Return the
+** address of the new instruction.
+*/
+int sqlite3CodeOnce(Parse *pParse){
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
+ return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
+}
+
+/*
** This function is used by the implementation of the IN (...) operator.
** It's job is to find or create a b-tree structure that may be used
** either to test for membership of the (...) set or to iterate through
@@ -1433,6 +1444,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
int eType = 0; /* Type of RHS table. IN_INDEX_* */
int iTab = pParse->nTab++; /* Cursor of the RHS table */
int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
assert( pX->op==TK_IN );
@@ -1443,7 +1455,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
sqlite3 *db = pParse->db; /* Database connection */
- Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
Table *pTab; /* Table <table>. */
Expr *pExpr; /* Expression <column> */
int iCol; /* Index of column <column> */
@@ -1468,10 +1479,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
*/
assert(v);
if( iCol<0 ){
- int iMem = ++pParse->nMem;
int iAddr;
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ iAddr = sqlite3CodeOnce(pParse);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
@@ -1497,12 +1507,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
){
- int iMem = ++pParse->nMem;
int iAddr;
char *pKey;
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ iAddr = sqlite3CodeOnce(pParse);
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
pKey,P4_KEYINFO_HANDOFF);
@@ -1512,6 +1521,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
sqlite3VdbeJumpHere(v, iAddr);
if( prNotFound && !pTab->aCol[iCol].notNull ){
*prNotFound = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
}
}
}
@@ -1527,6 +1537,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
eType = IN_INDEX_EPH;
if( prNotFound ){
*prNotFound = rMayHaveNull = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
}else{
testcase( pParse->nQueryLoop>(double)1 );
pParse->nQueryLoop = (double)1;
@@ -1599,9 +1610,8 @@ int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
- int mem = ++pParse->nMem;
- testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
+ if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
+ testAddr = sqlite3CodeOnce(pParse);
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -2939,6 +2949,264 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
return inReg;
}
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+/*
+** Generate a human-readable explanation of an expression tree.
+*/
+void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
+ int op; /* The opcode being coded */
+ const char *zBinOp = 0; /* Binary operator */
+ const char *zUniOp = 0; /* Unary operator */
+ if( pExpr==0 ){
+ op = TK_NULL;
+ }else{
+ op = pExpr->op;
+ }
+ switch( op ){
+ case TK_AGG_COLUMN: {
+ sqlite3ExplainPrintf(pOut, "AGG{%d:%d}",
+ pExpr->iTable, pExpr->iColumn);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iTable<0 ){
+ /* This only happens when coding check constraints */
+ sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn);
+ }else{
+ sqlite3ExplainPrintf(pOut, "{%d:%d}",
+ pExpr->iTable, pExpr->iColumn);
+ }
+ break;
+ }
+ case TK_INTEGER: {
+ if( pExpr->flags & EP_IntValue ){
+ sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue);
+ }else{
+ sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_FLOATING_POINT
+ case TK_FLOAT: {
+ sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_STRING: {
+ sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken);
+ break;
+ }
+ case TK_NULL: {
+ sqlite3ExplainPrintf(pOut,"NULL");
+ break;
+ }
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB: {
+ sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_VARIABLE: {
+ sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)",
+ pExpr->u.zToken, pExpr->iColumn);
+ break;
+ }
+ case TK_REGISTER: {
+ sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable);
+ break;
+ }
+ case TK_AS: {
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ break;
+ }
+#ifndef SQLITE_OMIT_CAST
+ case TK_CAST: {
+ /* Expressions of the form: CAST(pLeft AS token) */
+ const char *zAff = "unk";
+ switch( sqlite3AffinityType(pExpr->u.zToken) ){
+ case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
+ case SQLITE_AFF_NONE: zAff = "NONE"; break;
+ case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
+ case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break;
+ case SQLITE_AFF_REAL: zAff = "REAL"; break;
+ }
+ sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#endif /* SQLITE_OMIT_CAST */
+ case TK_LT: zBinOp = "LT"; break;
+ case TK_LE: zBinOp = "LE"; break;
+ case TK_GT: zBinOp = "GT"; break;
+ case TK_GE: zBinOp = "GE"; break;
+ case TK_NE: zBinOp = "NE"; break;
+ case TK_EQ: zBinOp = "EQ"; break;
+ case TK_IS: zBinOp = "IS"; break;
+ case TK_ISNOT: zBinOp = "ISNOT"; break;
+ case TK_AND: zBinOp = "AND"; break;
+ case TK_OR: zBinOp = "OR"; break;
+ case TK_PLUS: zBinOp = "ADD"; break;
+ case TK_STAR: zBinOp = "MUL"; break;
+ case TK_MINUS: zBinOp = "SUB"; break;
+ case TK_REM: zBinOp = "REM"; break;
+ case TK_BITAND: zBinOp = "BITAND"; break;
+ case TK_BITOR: zBinOp = "BITOR"; break;
+ case TK_SLASH: zBinOp = "DIV"; break;
+ case TK_LSHIFT: zBinOp = "LSHIFT"; break;
+ case TK_RSHIFT: zBinOp = "RSHIFT"; break;
+ case TK_CONCAT: zBinOp = "CONCAT"; break;
+
+ case TK_UMINUS: zUniOp = "UMINUS"; break;
+ case TK_UPLUS: zUniOp = "UPLUS"; break;
+ case TK_BITNOT: zUniOp = "BITNOT"; break;
+ case TK_NOT: zUniOp = "NOT"; break;
+ case TK_ISNULL: zUniOp = "ISNULL"; break;
+ case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+
+ case TK_AGG_FUNCTION:
+ case TK_CONST_FUNC:
+ case TK_FUNCTION: {
+ ExprList *pFarg; /* List of function arguments */
+ if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ pFarg = 0;
+ }else{
+ pFarg = pExpr->x.pList;
+ }
+ sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
+ op==TK_AGG_FUNCTION ? "AGG_" : "",
+ pExpr->u.zToken);
+ if( pFarg ){
+ sqlite3ExplainExprList(pOut, pFarg);
+ }
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS: {
+ sqlite3ExplainPrintf(pOut, "EXISTS(");
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ sqlite3ExplainPrintf(pOut,")");
+ break;
+ }
+ case TK_SELECT: {
+ sqlite3ExplainPrintf(pOut, "(");
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+ case TK_IN: {
+ sqlite3ExplainPrintf(pOut, "IN(");
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ",");
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ }else{
+ sqlite3ExplainExprList(pOut, pExpr->x.pList);
+ }
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#endif /* SQLITE_OMIT_SUBQUERY */
+
+ /*
+ ** x BETWEEN y AND z
+ **
+ ** This is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** X is stored in pExpr->pLeft.
+ ** Y is stored in pExpr->pList->a[0].pExpr.
+ ** Z is stored in pExpr->pList->a[1].pExpr.
+ */
+ case TK_BETWEEN: {
+ Expr *pX = pExpr->pLeft;
+ Expr *pY = pExpr->x.pList->a[0].pExpr;
+ Expr *pZ = pExpr->x.pList->a[1].pExpr;
+ sqlite3ExplainPrintf(pOut, "BETWEEN(");
+ sqlite3ExplainExpr(pOut, pX);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExpr(pOut, pY);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExpr(pOut, pZ);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+ case TK_TRIGGER: {
+ /* If the opcode is TK_TRIGGER, then the expression is a reference
+ ** to a column in the new.* or old.* pseudo-tables available to
+ ** trigger programs. In this case Expr.iTable is set to 1 for the
+ ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
+ ** is set to the column of the pseudo-table to read, or to -1 to
+ ** read the rowid field.
+ */
+ sqlite3ExplainPrintf(pOut, "%s(%d)",
+ pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
+ break;
+ }
+ case TK_CASE: {
+ sqlite3ExplainPrintf(pOut, "CASE(");
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExprList(pOut, pExpr->x.pList);
+ break;
+ }
+#ifndef SQLITE_OMIT_TRIGGER
+ case TK_RAISE: {
+ const char *zType = "unk";
+ switch( pExpr->affinity ){
+ case OE_Rollback: zType = "rollback"; break;
+ case OE_Abort: zType = "abort"; break;
+ case OE_Fail: zType = "fail"; break;
+ case OE_Ignore: zType = "ignore"; break;
+ }
+ sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken);
+ break;
+ }
+#endif
+ }
+ if( zBinOp ){
+ sqlite3ExplainPrintf(pOut,"%s(", zBinOp);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut,",");
+ sqlite3ExplainExpr(pOut, pExpr->pRight);
+ sqlite3ExplainPrintf(pOut,")");
+ }else if( zUniOp ){
+ sqlite3ExplainPrintf(pOut,"%s(", zUniOp);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut,")");
+ }
+}
+#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
+
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+/*
+** Generate a human-readable explanation of an expression list.
+*/
+void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
+ int i;
+ if( pList==0 || pList->nExpr==0 ){
+ sqlite3ExplainPrintf(pOut, "(empty-list)");
+ return;
+ }else if( pList->nExpr==1 ){
+ sqlite3ExplainExpr(pOut, pList->a[0].pExpr);
+ }else{
+ sqlite3ExplainPush(pOut);
+ for(i=0; i<pList->nExpr; i++){
+ sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
+ sqlite3ExplainPush(pOut);
+ sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
+ sqlite3ExplainPop(pOut);
+ if( i<pList->nExpr-1 ){
+ sqlite3ExplainNL(pOut);
+ }
+ }
+ sqlite3ExplainPop(pOut);
+ }
+}
+#endif /* SQLITE_DEBUG */
+
/*
** Return TRUE if pExpr is an constant expression that is appropriate
** for factoring out of a loop. Appropriate expressions are:
@@ -3762,3 +4030,11 @@ void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
pParse->iRangeReg = iReg;
}
}
+
+/*
+** Mark all temporary registers as being unavailable for reuse.
+*/
+void sqlite3ClearTempRegCache(Parse *pParse){
+ pParse->nTempReg = 0;
+ pParse->nRangeReg = 0;
+}
diff --git a/src/global.c b/src/global.c
index b8a8a49d3..7de066825 100644
--- a/src/global.c
+++ b/src/global.c
@@ -147,7 +147,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
- {0,0,0,0,0,0,0,0,0,0,0}, /* pcache2 */
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
(void*)0, /* pHeap */
0, /* nHeap */
0, 0, /* mnHeap, mxHeap */
diff --git a/src/insert.c b/src/insert.c
index eca3c12dd..dadb10acd 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -239,6 +239,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
diff --git a/src/loadext.c b/src/loadext.c
index e9c97adff..3fcf5008c 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -625,6 +625,7 @@ void sqlite3_reset_auto_extension(void){
void sqlite3AutoLoadExtensions(sqlite3 *db){
int i;
int go = 1;
+ int rc;
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
wsdAutoextInit;
@@ -647,8 +648,8 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
}
sqlite3_mutex_leave(mutex);
zErrmsg = 0;
- if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
- sqlite3Error(db, SQLITE_ERROR,
+ if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
+ sqlite3Error(db, rc,
"automatic extension loading failed: %s", zErrmsg);
go = 0;
}
diff --git a/src/main.c b/src/main.c
index 8562a9072..690b73c2e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -239,8 +239,8 @@ int sqlite3_initialize(void){
*/
#ifdef SQLITE_EXTRA_INIT
if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
- int SQLITE_EXTRA_INIT(void);
- rc = SQLITE_EXTRA_INIT();
+ int SQLITE_EXTRA_INIT(const char*);
+ rc = SQLITE_EXTRA_INIT(0);
}
#endif
@@ -257,6 +257,10 @@ int sqlite3_initialize(void){
*/
int sqlite3_shutdown(void){
if( sqlite3GlobalConfig.isInit ){
+#ifdef SQLITE_EXTRA_SHUTDOWN
+ void SQLITE_EXTRA_SHUTDOWN(void);
+ SQLITE_EXTRA_SHUTDOWN();
+#endif
sqlite3_os_end();
sqlite3_reset_auto_extension();
sqlite3GlobalConfig.isInit = 0;
@@ -2239,10 +2243,13 @@ static int openDatabase(
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
*/
- sqlite3AutoLoadExtensions(db);
rc = sqlite3_errcode(db);
- if( rc!=SQLITE_OK ){
- goto opendb_out;
+ if( rc==SQLITE_OK ){
+ sqlite3AutoLoadExtensions(db);
+ rc = sqlite3_errcode(db);
+ if( rc!=SQLITE_OK ){
+ goto opendb_out;
+ }
}
#ifdef SQLITE_ENABLE_FTS1
@@ -2936,6 +2943,22 @@ int sqlite3_test_control(int op, ...){
break;
}
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ /* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
+ ** sqlite3_stmt*,const char**);
+ **
+ ** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
+ ** a string that describes the optimized parse tree. This test-control
+ ** returns a pointer to that string.
+ */
+ case SQLITE_TESTCTRL_EXPLAIN_STMT: {
+ sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
+ const char **pzRet = va_arg(ap, const char**);
+ *pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
+ break;
+ }
+#endif
+
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --git a/src/mem1.c b/src/mem1.c
index e0d1dd6e8..bf84ce090 100644
--- a/src/mem1.c
+++ b/src/mem1.c
@@ -26,10 +26,47 @@
*/
#ifdef SQLITE_SYSTEM_MALLOC
+/*
+** Windows systems have malloc_usable_size() but it is called _msize()
+*/
+#if !defined(HAVE_MALLOC_USABLE_SIZE) && SQLITE_OS_WIN
+# define HAVE_MALLOC_USABLE_SIZE 1
+# define malloc_usable_size _msize
+#endif
+
+#if defined(__APPLE__)
+
+/*
+** Use the zone allocator available on apple products
+*/
+#include <sys/sysctl.h>
+#include <malloc/malloc.h>
+#include <libkern/OSAtomic.h>
+static malloc_zone_t* _sqliteZone_;
+#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
+#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
+#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
+#define SQLITE_MALLOCSIZE(x) \
+ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
+
+#else /* if not __APPLE__ */
+
+/*
+** Use standard C library malloc and free on non-Apple systems.
+*/
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
+
#ifdef HAVE_MALLOC_USABLE_SIZE
#include <malloc.h>
+#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
+#else
+#undef SQLITE_MALLOCSIZE
#endif
+#endif /* __APPLE__ or not __APPLE__ */
+
/*
** Like malloc(), but remember the size of the allocation
** so that we can find it later using sqlite3MemSize().
@@ -39,8 +76,8 @@
** routines.
*/
static void *sqlite3MemMalloc(int nByte){
-#ifdef HAVE_MALLOC_USABLE_SIZE
- void *p = malloc( nByte );
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_MALLOC( nByte );
if( p==0 ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
@@ -50,7 +87,7 @@ static void *sqlite3MemMalloc(int nByte){
sqlite3_int64 *p;
assert( nByte>0 );
nByte = ROUND8(nByte);
- p = malloc( nByte+8 );
+ p = SQLITE_MALLOC( nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -71,13 +108,13 @@ static void *sqlite3MemMalloc(int nByte){
** by higher-level routines.
*/
static void sqlite3MemFree(void *pPrior){
-#if HAVE_MALLOC_USABLE_SIZE
- free(pPrior);
+#ifdef SQLITE_MALLOCSIZE
+ SQLITE_FREE(pPrior);
#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 );
p--;
- free(p);
+ SQLITE_FREE(p);
#endif
}
@@ -86,8 +123,8 @@ static void sqlite3MemFree(void *pPrior){
** or xRealloc().
*/
static int sqlite3MemSize(void *pPrior){
-#if HAVE_MALLOC_USABLE_SIZE
- return pPrior ? (int)malloc_usable_size(pPrior) : 0;
+#ifdef SQLITE_MALLOCSIZE
+ return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
#else
sqlite3_int64 *p;
if( pPrior==0 ) return 0;
@@ -108,13 +145,13 @@ static int sqlite3MemSize(void *pPrior){
** routines and redirected to xFree.
*/
static void *sqlite3MemRealloc(void *pPrior, int nByte){
-#if HAVE_MALLOC_USABLE_SIZE
- void *p = realloc(pPrior, nByte);
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_REALLOC(pPrior, nByte);
if( p==0 ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM,
"failed memory resize %u to %u bytes",
- malloc_usable_size(pPrior), nByte);
+ SQLITE_MALLOCSIZE(pPrior), nByte);
}
return p;
#else
@@ -122,7 +159,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
assert( pPrior!=0 && nByte>0 );
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
p--;
- p = realloc(p, nByte+8 );
+ p = SQLITE_REALLOC(p, nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -147,6 +184,34 @@ static int sqlite3MemRoundup(int n){
** Initialize this module.
*/
static int sqlite3MemInit(void *NotUsed){
+#if defined(__APPLE__)
+ int cpuCount;
+ size_t len;
+ if( _sqliteZone_ ){
+ return SQLITE_OK;
+ }
+ len = sizeof(cpuCount);
+ /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
+ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
+ if( cpuCount>1 ){
+ /* defer MT decisions to system malloc */
+ _sqliteZone_ = malloc_default_zone();
+ }else{
+ /* only 1 core, use our own zone to contention over global locks,
+ ** e.g. we have our own dedicated locks */
+ bool success;
+ malloc_zone_t* newzone = malloc_create_zone(4096, 0);
+ malloc_set_zone_name(newzone, "Sqlite_Heap");
+ do{
+ success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
+ (void * volatile *)&_sqliteZone_);
+ }while(!_sqliteZone_);
+ if( !success ){
+ /* somebody registered a zone first */
+ malloc_destroy_zone(newzone);
+ }
+ }
+#endif
UNUSED_PARAMETER(NotUsed);
return SQLITE_OK;
}
diff --git a/src/os.h b/src/os.h
index 7f17c203e..80afc516f 100644
--- a/src/os.h
+++ b/src/os.h
@@ -66,17 +66,6 @@
#endif
/*
-** Determine if we are dealing with WindowsCE - which has a much
-** reduced API.
-*/
-#if defined(_WIN32_WCE)
-# define SQLITE_OS_WINCE 1
-#else
-# define SQLITE_OS_WINCE 0
-#endif
-
-
-/*
** Define the maximum size of a temporary filename
*/
#if SQLITE_OS_WIN
@@ -100,6 +89,25 @@
# define SQLITE_TEMPNAME_SIZE 200
#endif
+/*
+** Determine if we are dealing with Windows NT.
+*/
+#if defined(_WIN32_WINNT)
+# define SQLITE_OS_WINNT 1
+#else
+# define SQLITE_OS_WINNT 0
+#endif
+
+/*
+** Determine if we are dealing with WindowsCE - which has a much
+** reduced API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
/* If the SET_FULLSYNC macro is not defined above, then make it
** a no-op
*/
diff --git a/src/os_unix.c b/src/os_unix.c
index 51d144906..ee5971f10 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -206,6 +206,7 @@ struct UnixUnusedFd {
typedef struct unixFile unixFile;
struct unixFile {
sqlite3_io_methods const *pMethod; /* Always the first entry */
+ sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */
unsigned char eFileLock; /* The type of lock held on this fd */
@@ -2014,8 +2015,8 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
rc = osRmdir(zLockFile);
if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
if( rc<0 ){
- int rc = 0;
int tErrno = errno;
+ rc = 0;
if( ENOENT != tErrno ){
rc = SQLITE_IOERR_UNLOCK;
}
@@ -3533,6 +3534,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
return SQLITE_OK;
}
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
+ return SQLITE_OK;
+ }
#ifndef NDEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
@@ -4560,6 +4565,7 @@ static int fillInUnixFile(
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
+ pNew->pVfs = pVfs;
pNew->zPath = zFilename;
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
pNew->ctrlFlags = UNIXFILE_EXCL;
@@ -4899,7 +4905,7 @@ static int findCreateFileMode(
*/
nDb = sqlite3Strlen30(zPath) - 1;
#ifdef SQLITE_ENABLE_8_3_NAMES
- while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
+ while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
#else
while( zPath[nDb]!='-' ){
diff --git a/src/os_win.c b/src/os_win.c
index 62868ba50..ab70eebbf 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -181,7 +181,7 @@ static int sqlite3_os_type = 0;
# define SQLITE_WIN32_HAS_ANSI
#endif
-#if SQLITE_OS_WINCE || defined(_WIN32_WINNT)
+#if SQLITE_OS_WINCE || SQLITE_OS_WINNT
# define SQLITE_WIN32_HAS_WIDE
#endif
@@ -918,6 +918,9 @@ static LPWSTR utf8ToUnicode(const char *zFilename){
LPWSTR zWideFilename;
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) );
if( zWideFilename==0 ){
return 0;
@@ -940,6 +943,9 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){
char *zFilename;
nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -967,6 +973,9 @@ static LPWSTR mbcsToUnicode(const char *zFilename){
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
0)*sizeof(WCHAR);
+ if( nByte==0 ){
+ return 0;
+ }
zMbcsFilename = sqlite3_malloc( nByte*sizeof(zMbcsFilename[0]) );
if( zMbcsFilename==0 ){
return 0;
@@ -993,6 +1002,9 @@ static char *unicodeToMbcs(LPCWSTR zWideFilename){
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -1170,12 +1182,14 @@ static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
** to see if it should be retried. Return TRUE to retry. Return FALSE
** to give up with an error.
*/
-static int retryIoerr(int *pnRetry){
- DWORD e;
+static int retryIoerr(int *pnRetry, DWORD *pError){
+ DWORD e = osGetLastError();
if( *pnRetry>=win32IoerrRetry ){
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
- e = osGetLastError();
if( e==ERROR_ACCESS_DENIED ||
e==ERROR_LOCK_VIOLATION ||
e==ERROR_SHARING_VIOLATION ){
@@ -1183,6 +1197,9 @@ static int retryIoerr(int *pnRetry){
++*pnRetry;
return 1;
}
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
@@ -1539,6 +1556,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
+ DWORD lastErrno; /* Value returned by GetLastError() */
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
@@ -1551,8 +1569,10 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
** GetLastError().
*/
dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
- if( (dwRet==INVALID_SET_FILE_POINTER && osGetLastError()!=NO_ERROR) ){
- pFile->lastErrno = osGetLastError();
+
+ if( (dwRet==INVALID_SET_FILE_POINTER
+ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
"seekWinFile", pFile->zPath);
return 1;
@@ -1628,8 +1648,9 @@ static int winRead(
return SQLITE_FULL;
}
while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
- if( retryIoerr(&nRetry) ) continue;
- pFile->lastErrno = osGetLastError();
+ DWORD lastErrno;
+ if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ pFile->lastErrno = lastErrno;
return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
"winRead", pFile->zPath);
}
@@ -1669,10 +1690,11 @@ static int winWrite(
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
+ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
while( nRem>0 ){
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
- if( retryIoerr(&nRetry) ) continue;
+ if( retryIoerr(&nRetry, &lastErrno) ) continue;
break;
}
if( nWrite<=0 ) break;
@@ -1680,7 +1702,7 @@ static int winWrite(
nRem -= nWrite;
}
if( nRem>0 ){
- pFile->lastErrno = osGetLastError();
+ pFile->lastErrno = lastErrno;
rc = 1;
}
}
@@ -1810,15 +1832,15 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
DWORD upperBits;
DWORD lowerBits;
winFile *pFile = (winFile*)id;
- DWORD error;
+ DWORD lastErrno;
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
lowerBits = osGetFileSize(pFile->h, &upperBits);
if( (lowerBits == INVALID_FILE_SIZE)
- && ((error = osGetLastError()) != NO_ERROR) )
+ && ((lastErrno = osGetLastError())!=NO_ERROR) )
{
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
return winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
"winFileSize", pFile->zPath);
}
@@ -1869,6 +1891,7 @@ static int getReadLock(winFile *pFile){
*/
static int unlockReadLock(winFile *pFile){
int res;
+ DWORD lastErrno;
if( isNT() ){
res = osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
@@ -1878,8 +1901,8 @@ static int unlockReadLock(winFile *pFile){
res = osUnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
#endif
}
- if( res==0 && osGetLastError()!=ERROR_NOT_LOCKED ){
- pFile->lastErrno = osGetLastError();
+ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
"unlockReadLock", pFile->zPath);
}
@@ -1918,7 +1941,7 @@ static int winLock(sqlite3_file *id, int locktype){
int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
- DWORD error = NO_ERROR;
+ DWORD lastErrno = NO_ERROR;
assert( id!=0 );
OSTRACE(("LOCK %d %d was %d(%d)\n",
@@ -1960,7 +1983,7 @@ static int winLock(sqlite3_file *id, int locktype){
}
gotPendingLock = res;
if( !res ){
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -1972,7 +1995,7 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = SHARED_LOCK;
}else{
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -1984,7 +2007,7 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = RESERVED_LOCK;
}else{
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -2005,8 +2028,8 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
- error = osGetLastError();
- OSTRACE(("error-code = %d\n", error));
+ lastErrno = osGetLastError();
+ OSTRACE(("error-code = %d\n", lastErrno));
getReadLock(pFile);
}
}
@@ -2026,7 +2049,7 @@ static int winLock(sqlite3_file *id, int locktype){
}else{
OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h,
locktype, newLocktype));
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
}
pFile->locktype = (u8)newLocktype;
@@ -2145,6 +2168,10 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
}
return SQLITE_OK;
}
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("win32");
+ return SQLITE_OK;
+ }
case SQLITE_FCNTL_SYNC_OMITTED: {
return SQLITE_OK;
}
@@ -2964,6 +2991,7 @@ static int winOpen(
int *pOutFlags /* Status return flags */
){
HANDLE h;
+ DWORD lastErrno;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwCreationDisposition;
@@ -3100,7 +3128,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
+ retryIoerr(&cnt, &lastErrno) ){}
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ANSI version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
@@ -3113,7 +3141,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
+ retryIoerr(&cnt, &lastErrno) ){}
#endif
}
@@ -3124,7 +3152,7 @@ static int winOpen(
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
- pFile->lastErrno = osGetLastError();
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
sqlite3_free(zConverted);
if( isReadWrite && !isExclusive ){
@@ -3191,6 +3219,7 @@ static int winDelete(
){
int cnt = 0;
int rc;
+ DWORD lastErrno;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
@@ -3203,7 +3232,7 @@ static int winDelete(
if( isNT() ){
rc = 1;
while( osGetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
+ (rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
rc = rc ? SQLITE_OK : SQLITE_ERROR;
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ANSI version of these Windows API do not exist for WINCE,
@@ -3213,12 +3242,12 @@ static int winDelete(
}else{
rc = 1;
while( osGetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
+ (rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
rc = rc ? SQLITE_OK : SQLITE_ERROR;
#endif
}
if( rc ){
- rc = winLogError(SQLITE_IOERR_DELETE, osGetLastError(),
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno,
"winDelete", zFilename);
}else{
logIoerr(cnt);
@@ -3239,6 +3268,7 @@ static int winAccess(
){
DWORD attr;
int rc = 0;
+ DWORD lastErrno;
void *zConverted;
UNUSED_PARAMETER(pVfs);
@@ -3253,7 +3283,7 @@ static int winAccess(
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt) ){}
+ &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
@@ -3266,7 +3296,6 @@ static int winAccess(
attr = sAttrData.dwFileAttributes;
}
}else{
- DWORD lastErrno = osGetLastError();
logIoerr(cnt);
if( lastErrno!=ERROR_FILE_NOT_FOUND ){
winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename);
diff --git a/src/parse.y b/src/parse.y
index b838215d4..2b8e9170f 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -394,6 +394,9 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
cmd ::= select(X). {
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
sqlite3Select(pParse, X, &dest);
+ sqlite3ExplainBegin(pParse->pVdbe);
+ sqlite3ExplainSelect(pParse->pVdbe, X);
+ sqlite3ExplainFinish(pParse->pVdbe);
sqlite3SelectDelete(pParse->db, X);
}
diff --git a/src/printf.c b/src/printf.c
index 0babee514..58cfd2bd4 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -136,7 +136,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
/*
** Append N space characters to the given string buffer.
*/
-static void appendSpace(StrAccum *pAccum, int N){
+void sqlite3AppendSpace(StrAccum *pAccum, int N){
static const char zSpaces[] = " ";
while( N>=(int)sizeof(zSpaces)-1 ){
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
@@ -664,7 +664,7 @@ void sqlite3VXPrintf(
register int nspace;
nspace = width-length;
if( nspace>0 ){
- appendSpace(pAccum, nspace);
+ sqlite3AppendSpace(pAccum, nspace);
}
}
if( length>0 ){
@@ -674,7 +674,7 @@ void sqlite3VXPrintf(
register int nspace;
nspace = width-length;
if( nspace>0 ){
- appendSpace(pAccum, nspace);
+ sqlite3AppendSpace(pAccum, nspace);
}
}
sqlite3_free(zExtra);
diff --git a/src/resolve.c b/src/resolve.c
index 6d857f007..3da48136f 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -799,7 +799,7 @@ static int resolveCompoundOrderBy(
pE->pColl = pColl;
pE->flags |= EP_IntValue | flags;
pE->u.iValue = iCol;
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
pItem->done = 1;
}else{
moreToDo = 1;
@@ -848,12 +848,12 @@ int sqlite3ResolveOrderGroupBy(
pEList = pSelect->pEList;
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
- if( pItem->iCol ){
- if( pItem->iCol>pEList->nExpr ){
+ if( pItem->iOrderByCol ){
+ if( pItem->iOrderByCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
+ resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType);
}
}
return 0;
@@ -900,7 +900,7 @@ static int resolveOrderGroupBy(
** a copy of the iCol-th result-set column. The subsequent call to
** sqlite3ResolveOrderGroupBy() will convert the expression to a
** copy of the iCol-th result-set expression. */
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
continue;
}
if( sqlite3ExprIsInteger(pE, &iCol) ){
@@ -911,12 +911,12 @@ static int resolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, nResult);
return 1;
}
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
continue;
}
/* Otherwise, treat the ORDER BY term as an ordinary expression */
- pItem->iCol = 0;
+ pItem->iOrderByCol = 0;
if( sqlite3ResolveExprNames(pNC, pE) ){
return 1;
}
diff --git a/src/select.c b/src/select.c
index 571a77822..188050e8d 100644
--- a/src/select.c
+++ b/src/select.c
@@ -2219,8 +2219,8 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
- assert( pItem->iCol>0 );
- if( pItem->iCol==i ) break;
+ assert( pItem->iOrderByCol>0 );
+ if( pItem->iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
@@ -2228,7 +2228,7 @@ static int multiSelectOrderBy(
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
- pOrderBy->a[nOrderBy++].iCol = (u16)i;
+ pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
}
}
}
@@ -2244,8 +2244,8 @@ static int multiSelectOrderBy(
if( aPermute ){
struct ExprList_item *pItem;
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
- assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
- aPermute[i] = pItem->iCol - 1;
+ assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
+ aPermute[i] = pItem->iOrderByCol - 1;
}
pKeyMerge =
sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
@@ -2588,9 +2588,8 @@ static void substSelect(
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
-** This routine attempts to flatten subqueries in order to speed
-** execution. It returns 1 if it makes changes and 0 if no flattening
-** occurs.
+** This routine attempts to flatten subqueries as a performance optimization.
+** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:
@@ -2632,7 +2631,10 @@ static void substSelect(
** (6) The subquery does not use aggregates or the outer query is not
** DISTINCT.
**
-** (7) The subquery has a FROM clause.
+** (7) The subquery has a FROM clause. TODO: For subqueries without
+** A FROM clause, consider adding a FROM close with the special
+** table sqlite_once that consists of a single row containing a
+** single NULL.
**
** (8) The subquery does not use LIMIT or the outer query is not a join.
**
@@ -2665,11 +2667,14 @@ static void substSelect(
**
** * is not itself part of a compound select,
** * is not an aggregate or DISTINCT query, and
-** * has no other tables or sub-selects in the FROM clause.
+** * is not a join
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
-** LIMIT and OFFSET clauses.
+** LIMIT and OFFSET clauses. The subquery cannot use any compound
+** operator other than UNION ALL because all the other compound
+** operators have an implied DISTINCT which is disallowed by
+** restriction (4).
**
** (18) If the sub-query is a compound select, then all terms of the
** ORDER by clause of the parent must be simple references to
@@ -2681,7 +2686,7 @@ static void substSelect(
** (20) If the sub-query is a compound select, then it must not use
** an ORDER BY clause. Ticket #3773. We could relax this constraint
** somewhat by saying that the terms of the ORDER BY clause must
-** appear as unmodified result columns in the outer query. But
+** appear as unmodified result columns in the outer query. But we
** have other optimizations in mind to deal with that case.
**
** (21) The subquery does not use LIMIT or the outer query is not
@@ -2810,19 +2815,21 @@ static int flattenSubquery(
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
+ assert( pSub->pSrc!=0 );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
- || NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1
+ || pSub1->pSrc->nSrc<1
){
return 0;
}
+ testcase( pSub1->pSrc->nSrc>1 );
}
/* Restriction 18. */
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
- if( p->pOrderBy->a[ii].iCol==0 ) return 0;
+ if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0;
}
}
}
@@ -3845,12 +3852,11 @@ int sqlite3Select(
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
- if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
+ if( pItem->isCorrelated==0 ){
/* If the subquery is no correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
- int regOnce = ++pParse->nMem;
- onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
+ onceAddr = sqlite3CodeOnce(pParse);
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
@@ -3860,7 +3866,7 @@ int sqlite3Select(
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
-
+ sqlite3ClearTempRegCache(pParse);
}
if( /*pParse->nErr ||*/ db->mallocFailed ){
goto select_end;
@@ -4155,6 +4161,7 @@ int sqlite3Select(
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
VdbeComment((v, "indicate accumulator empty"));
+ sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
@@ -4494,98 +4501,98 @@ select_end:
return rc;
}
-#if defined(SQLITE_DEBUG)
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
-*******************************************************************************
-** The following code is used for testing and debugging only. The code
-** that follows does not appear in normal builds.
-**
-** These routines are used to print out the content of all or part of a
-** parse structures such as Select or Expr. Such printouts are useful
-** for helping to understand what is happening inside the code generator
-** during the execution of complex SELECT statements.
-**
-** These routine are not called anywhere from within the normal
-** code base. Then are intended to be called from within the debugger
-** or from temporary "printf" statements inserted for debugging.
+** Generate a human-readable description of a the Select object.
*/
-void sqlite3PrintExpr(Expr *p){
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- sqlite3DebugPrintf("(%s", p->u.zToken);
- }else{
- sqlite3DebugPrintf("(%d", p->op);
- }
- if( p->pLeft ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pLeft);
- }
- if( p->pRight ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pRight);
- }
- sqlite3DebugPrintf(")");
-}
-void sqlite3PrintExprList(ExprList *pList){
- int i;
- for(i=0; i<pList->nExpr; i++){
- sqlite3PrintExpr(pList->a[i].pExpr);
- if( i<pList->nExpr-1 ){
- sqlite3DebugPrintf(", ");
+static void explainOneSelect(Vdbe *pVdbe, Select *p){
+ sqlite3ExplainPrintf(pVdbe, "SELECT ");
+ if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
+ if( p->selFlags & SF_Distinct ){
+ sqlite3ExplainPrintf(pVdbe, "DISTINCT ");
+ }
+ if( p->selFlags & SF_Aggregate ){
+ sqlite3ExplainPrintf(pVdbe, "agg_flag ");
}
+ sqlite3ExplainNL(pVdbe);
+ sqlite3ExplainPrintf(pVdbe, " ");
}
-}
-void sqlite3PrintSelect(Select *p, int indent){
- sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
- sqlite3PrintExprList(p->pEList);
- sqlite3DebugPrintf("\n");
- if( p->pSrc ){
- char *zPrefix;
+ sqlite3ExplainExprList(pVdbe, p->pEList);
+ sqlite3ExplainNL(pVdbe);
+ if( p->pSrc && p->pSrc->nSrc ){
int i;
- zPrefix = "FROM";
+ sqlite3ExplainPrintf(pVdbe, "FROM ");
+ sqlite3ExplainPush(pVdbe);
for(i=0; i<p->pSrc->nSrc; i++){
struct SrcList_item *pItem = &p->pSrc->a[i];
- sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
- zPrefix = "";
+ sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor);
if( pItem->pSelect ){
- sqlite3DebugPrintf("(\n");
- sqlite3PrintSelect(pItem->pSelect, indent+10);
- sqlite3DebugPrintf("%*s)", indent+8, "");
+ sqlite3ExplainSelect(pVdbe, pItem->pSelect);
+ if( pItem->pTab ){
+ sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName);
+ }
}else if( pItem->zName ){
- sqlite3DebugPrintf("%s", pItem->zName);
- }
- if( pItem->pTab ){
- sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
+ sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
}
if( pItem->zAlias ){
- sqlite3DebugPrintf(" AS %s", pItem->zAlias);
+ sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
}
- if( i<p->pSrc->nSrc-1 ){
- sqlite3DebugPrintf(",");
+ if( pItem->jointype & JT_LEFT ){
+ sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN");
}
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainNL(pVdbe);
}
+ sqlite3ExplainPop(pVdbe);
}
if( p->pWhere ){
- sqlite3DebugPrintf("%*s WHERE ", indent, "");
- sqlite3PrintExpr(p->pWhere);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "WHERE ");
+ sqlite3ExplainExpr(pVdbe, p->pWhere);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pGroupBy ){
- sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
- sqlite3PrintExprList(p->pGroupBy);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "GROUPBY ");
+ sqlite3ExplainExprList(pVdbe, p->pGroupBy);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pHaving ){
- sqlite3DebugPrintf("%*s HAVING ", indent, "");
- sqlite3PrintExpr(p->pHaving);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "HAVING ");
+ sqlite3ExplainExpr(pVdbe, p->pHaving);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pOrderBy ){
- sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
- sqlite3PrintExprList(p->pOrderBy);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "ORDERBY ");
+ sqlite3ExplainExprList(pVdbe, p->pOrderBy);
+ sqlite3ExplainNL(pVdbe);
+ }
+ if( p->pLimit ){
+ sqlite3ExplainPrintf(pVdbe, "LIMIT ");
+ sqlite3ExplainExpr(pVdbe, p->pLimit);
+ sqlite3ExplainNL(pVdbe);
+ }
+ if( p->pOffset ){
+ sqlite3ExplainPrintf(pVdbe, "OFFSET ");
+ sqlite3ExplainExpr(pVdbe, p->pOffset);
+ sqlite3ExplainNL(pVdbe);
+ }
+}
+void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
+ if( p==0 ){
+ sqlite3ExplainPrintf(pVdbe, "(null-select)");
+ return;
}
+ while( p->pPrior ) p = p->pPrior;
+ sqlite3ExplainPush(pVdbe);
+ while( p ){
+ explainOneSelect(pVdbe, p);
+ p = p->pNext;
+ if( p==0 ) break;
+ sqlite3ExplainNL(pVdbe);
+ sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op));
+ }
+ sqlite3ExplainPrintf(pVdbe, "END");
+ sqlite3ExplainPop(pVdbe);
}
+
/* End of the structure debug printing code
*****************************************************************************/
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
diff --git a/src/shell.c b/src/shell.c
index e33a0687b..31d3dd8fc 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1127,6 +1127,15 @@ static int shell_exec(
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
+ /* Output TESTCTRL_EXPLAIN text of requested */
+ if( pArg && pArg->mode==MODE_Explain ){
+ const char *zExplain = 0;
+ sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
+ if( zExplain && zExplain[0] ){
+ fprintf(pArg->out, "%s", zExplain);
+ }
+ }
+
/* perform the first step. this will tell us if we
** have a result set or not and how wide it is.
*/
@@ -1396,6 +1405,7 @@ static char zHelp[] =
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
+ ".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
;
@@ -2335,10 +2345,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
- printf("SQLite %s %s\n",
+ printf("SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
}else
+ if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){
+ const char *zDbName = nArg==2 ? azArg[1] : "main";
+ char *zVfsName = 0;
+ if( p->db ){
+ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
+ if( zVfsName ){
+ printf("%s\n", zVfsName);
+ sqlite3_free(zVfsName);
+ }
+ }
+ }else
+
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
int j;
assert( nArg<=ArraySize(azArg) );
@@ -2932,7 +2954,7 @@ int main(int argc, char **argv){
char *zHistory = 0;
int nHistory;
printf(
- "SQLite version %s %.19s\n"
+ "SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for instructions\n"
"Enter SQL statements terminated with a \";\"\n",
sqlite3_libversion(), sqlite3_sourceid()
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index bd5b41f43..0e0e3af6b 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -771,6 +771,17 @@ struct sqlite3_io_methods {
** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current
** transaction. This is used by VACUUM operations.
+**
+** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
+** all [VFSes] in the VFS stack. The names are of all VFS shims and the
+** final bottom-level VFS are written into memory obtained from
+** [sqlite3_malloc()] and the result is stored in the char* variable
+** that the fourth parameter of [sqlite3_file_control()] points to.
+** The caller is responsible for freeing the memory when done. As with
+** all file-control actions, there is no guarantee that this will actually
+** do anything. Callers should initialize the char* variable to a NULL
+** pointer in case this file-control is not implemented. This file-control
+** is intended for diagnostic use only.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
@@ -783,6 +794,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
#define SQLITE_FCNTL_OVERWRITE 11
+#define SQLITE_FCNTL_VFSNAME 12
/*
** CAPI3REF: Mutex Handle
@@ -5682,7 +5694,8 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ISKEYWORD 16
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
-#define SQLITE_TESTCTRL_LAST 18
+#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
+#define SQLITE_TESTCTRL_LAST 19
/*
** CAPI3REF: SQLite Runtime Status
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 4e00ad03c..8613d865a 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1707,10 +1707,10 @@ struct Expr {
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-
-#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
+#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
+#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
/*
** The following are the meanings of bits in the Expr.flags2 field.
@@ -1772,7 +1772,7 @@ struct ExprList {
char *zSpan; /* Original text of the expression */
u8 sortOrder; /* 1 for DESC or 0 for ASC */
u8 done; /* A flag to indicate when processing is finished */
- u16 iCol; /* For ORDER BY, column number in result set */
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */
};
@@ -2204,6 +2204,7 @@ struct Parse {
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
+ int nOnce; /* Number of OP_Once instructions so far */
int ckBase; /* Base register of data during check constraints */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
@@ -2650,6 +2651,29 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#if defined(SQLITE_TEST)
void *sqlite3TestTextToPtr(const char*);
#endif
+
+/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ void sqlite3ExplainBegin(Vdbe*);
+ void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
+ void sqlite3ExplainNL(Vdbe*);
+ void sqlite3ExplainPush(Vdbe*);
+ void sqlite3ExplainPop(Vdbe*);
+ void sqlite3ExplainFinish(Vdbe*);
+ void sqlite3ExplainSelect(Vdbe*, Select*);
+ void sqlite3ExplainExpr(Vdbe*, Expr*);
+ void sqlite3ExplainExprList(Vdbe*, ExprList*);
+ const char *sqlite3VdbeExplanation(Vdbe*);
+#else
+# define sqlite3ExplainBegin(X)
+# define sqlite3ExplainSelect(A,B)
+# define sqlite3ExplainExpr(A,B)
+# define sqlite3ExplainExprList(A,B)
+# define sqlite3ExplainFinish(X)
+# define sqlite3VdbeExplanation(X) 0
+#endif
+
+
void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...);
int sqlite3Dequote(char*);
@@ -2660,6 +2684,7 @@ int sqlite3GetTempReg(Parse*);
void sqlite3ReleaseTempReg(Parse*,int);
int sqlite3GetTempRange(Parse*,int);
void sqlite3ReleaseTempRange(Parse*,int,int);
+void sqlite3ClearTempRegCache(Parse*);
Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
Expr *sqlite3Expr(sqlite3*,int,const char*);
void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
@@ -2691,6 +2716,7 @@ void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
+int sqlite3CodeOnce(Parse *);
Bitvec *sqlite3BitvecCreate(u32);
int sqlite3BitvecTest(Bitvec*, u32);
@@ -3029,6 +3055,7 @@ int sqlite3OpenTempDatabase(Parse *);
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+void sqlite3AppendSpace(StrAccum*,int);
char *sqlite3StrAccumFinish(StrAccum*);
void sqlite3StrAccumReset(StrAccum*);
void sqlite3SelectDestInit(SelectDest*,int,int);
diff --git a/src/test1.c b/src/test1.c
index cf388d244..798366a8c 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -5237,6 +5237,39 @@ static int file_control_persist_wal(
/*
+** tclcmd: file_control_vfsname DB ?AUXDB?
+**
+** Return a string that describes the stack of VFSes.
+*/
+static int file_control_vfsname(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ const char *zDbName = "main";
+ char *zVfsName = 0;
+
+ if( objc!=2 && objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB ?AUXDB?", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
+ return TCL_ERROR;
+ }
+ if( objc==3 ){
+ zDbName = Tcl_GetString(objv[2]);
+ }
+ sqlite3_file_control(db, zDbName, SQLITE_FCNTL_VFSNAME,(void*)&zVfsName);
+ Tcl_AppendResult(interp, zVfsName, (char*)0);
+ sqlite3_free(zVfsName);
+ return TCL_OK;
+}
+
+
+/*
** tclcmd: sqlite3_vfs_list
**
** Return a tcl list containing the names of all registered vfs's.
@@ -6060,6 +6093,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
{ "file_control_persist_wal", file_control_persist_wal, 0 },
+ { "file_control_vfsname", file_control_vfsname, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
diff --git a/src/test_multiplex.c b/src/test_multiplex.c
index 2ca8bf8d5..f6540d82d 100644
--- a/src/test_multiplex.c
+++ b/src/test_multiplex.c
@@ -81,6 +81,9 @@
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */
+/* First chunk for rollback journal files */
+#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
+
/************************ Shim Definitions ******************************/
@@ -97,7 +100,7 @@
#endif
/* This used to be the default limit on number of chunks, but
-** it is no longer enforced. There is currently no limit to the
+** it is no longer enforced. There is currently no limit to the
** number of chunks.
**
** May be changed by calling the xFileControl() interface.
@@ -213,71 +216,6 @@ static int multiplexStrlen30(const char *z){
return 0x3fffffff & (int)(z2 - z);
}
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pOrigVfs->mxPathname characters. This function departs
-** from the traditional temporary name generation in the os_win
-** and os_unix VFS in several ways, but is necessary so that
-** the file name is known for temporary files (like those used
-** during vacuum.)
-**
-** N.B. This routine assumes your underlying VFS is ok with using
-** "/" as a directory seperator. This is the default for UNIXs
-** and is allowed (even mixed) for most versions of Windows.
-*/
-static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
- static char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i,j;
- int attempts = 0;
- int exists = 0;
- int rc = SQLITE_ERROR;
-
- /* Check that the output buffer is large enough for
- ** pVfs->mxPathname characters.
- */
- if( pOrigVfs->mxPathname <= nBuf ){
- char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
- if( zTmp==0 ) return SQLITE_NOMEM;
-
- /* sqlite3_temp_directory should always be less than
- ** pVfs->mxPathname characters.
- */
- sqlite3_snprintf(pOrigVfs->mxPathname,
- zTmp,
- "%s/",
- sqlite3_temp_directory ? sqlite3_temp_directory : ".");
- rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
- sqlite3_free(zTmp);
- if( rc ) return rc;
-
- /* Check that the output buffer is large enough for the temporary file
- ** name.
- */
- j = multiplexStrlen30(zBuf);
- if( (j + 8 + 1 + 3 + 1) <= nBuf ){
- /* Make 3 attempts to generate a unique name. */
- do {
- attempts++;
- sqlite3_randomness(8, &zBuf[j]);
- for(i=0; i<8; i++){
- unsigned char uc = (unsigned char)zBuf[j+i];
- zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
- }
- memcpy(&zBuf[j+i], ".tmp", 5);
- rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
- } while ( (rc==SQLITE_OK) && exists && (attempts<3) );
- if( rc==SQLITE_OK && exists ){
- rc = SQLITE_ERROR;
- }
- }
- }
-
- return rc;
-}
-
/* Compute the filename for the iChunk-th chunk
*/
static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
@@ -291,7 +229,7 @@ static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
pGroup->aReal = p;
pGroup->nReal = iChunk+1;
}
- if( pGroup->aReal[iChunk].z==0 ){
+ if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
char *z;
int n = pGroup->nName;
pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+4 );
@@ -304,6 +242,13 @@ static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
int i;
for(i=n-1; i>0 && i>=n-4 && z[i]!='.'; i--){}
if( i>=n-4 ) n = i+1;
+ if( pGroup->flags & (SQLITE_OPEN_MAIN_JOURNAL|SQLITE_OPEN_TEMP_JOURNAL) ){
+ /* The extensions on overflow files for main databases are 001, 002,
+ ** 003 and so forth. To avoid name collisions, add 400 to the
+ ** extensions of journal files so that they are 401, 402, 403, ....
+ */
+ iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
+ }
#endif
sqlite3_snprintf(4,&z[n],"%03d",iChunk);
}
@@ -322,6 +267,18 @@ static sqlite3_file *multiplexSubOpen(
){
sqlite3_file *pSubOpen = 0;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
+ ** part of a database journal are named db.401, db.402, and so on. A
+ ** database may therefore not grow to larger than 400 chunks. Attempting
+ ** to open chunk 401 indicates the database is full. */
+ if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
+ *rc = SQLITE_FULL;
+ return 0;
+ }
+#endif
+
*rc = multiplexSubFilename(pGroup, iChunk);
if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
@@ -454,6 +411,7 @@ static int multiplexOpen(
UNUSED_PARAMETER(pVfs);
memset(pConn, 0, pVfs->szOsFile);
+ assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
/* We need to create a group structure and manage
** access to this group of files.
@@ -461,23 +419,9 @@ static int multiplexOpen(
multiplexEnter();
pMultiplexOpen = (multiplexConn*)pConn;
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use. This will be handled by the
- ** original xOpen method. We just need to allocate space for
- ** it.
- */
- if( !zName ){
- zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
- if( zName==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
- }
- }
-
if( rc==SQLITE_OK ){
/* allocate space for group */
- nName = multiplexStrlen30(zName);
+ nName = zName ? multiplexStrlen30(zName) : 0;
sz = sizeof(multiplexGroup) /* multiplexGroup */
+ nName + 1; /* zName */
pGroup = sqlite3_malloc( sz );
@@ -488,32 +432,34 @@ static int multiplexOpen(
if( rc==SQLITE_OK ){
/* assign pointers to extra space allocated */
- char *p = (char *)&pGroup[1];
- pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
+ pMultiplexOpen->pGroup = pGroup;
pGroup->bEnabled = -1;
pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
- if( flags & SQLITE_OPEN_URI ){
- const char *zChunkSize;
- zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
- if( zChunkSize ){
- unsigned int n = 0;
- int i;
- for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
- n = n*10 + zChunkSize[i] - '0';
- }
- if( n>0 ){
- pGroup->szChunk = (n+0xffff)&~0xffff;
- }else{
- /* A zero or negative chunksize disabled the multiplexor */
- pGroup->bEnabled = 0;
+
+ if( zName ){
+ char *p = (char *)&pGroup[1];
+ if( flags & SQLITE_OPEN_URI ){
+ const char *zChunkSize;
+ zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
+ if( zChunkSize ){
+ unsigned int n = 0;
+ int i;
+ for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
+ n = n*10 + zChunkSize[i] - '0';
+ }
+ if( n>0 ){
+ pGroup->szChunk = (n+0xffff)&~0xffff;
+ }else{
+ /* A zero or negative chunksize disabled the multiplexor */
+ pGroup->bEnabled = 0;
+ }
}
}
+ pGroup->zName = p;
+ memcpy(pGroup->zName, zName, nName+1);
+ pGroup->nName = nName;
}
- pGroup->zName = p;
- /* save off base filename, name length, and original open flags */
- memcpy(pGroup->zName, zName, nName+1);
- pGroup->nName = nName;
pGroup->flags = flags;
rc = multiplexSubFilename(pGroup, 1);
if( rc==SQLITE_OK ){
@@ -524,7 +470,7 @@ static int multiplexOpen(
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
- if( rc2==SQLITE_OK ){
+ if( rc2==SQLITE_OK && zName ){
/* If the first overflow file exists and if the size of the main file
** is different from the chunk size, that means the chunk size is set
** set incorrectly. So fix it.
@@ -702,7 +648,7 @@ static int multiplexWrite(
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
}
}else{
- while( iAmt > 0 ){
+ while( rc==SQLITE_OK && iAmt>0 ){
int i = (int)(iOfst / pGroup->szChunk);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
if( pSubOpen ){
@@ -712,13 +658,9 @@ static int multiplexWrite(
iAmt -= extra;
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
iOfst % pGroup->szChunk);
- if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
- }else{
- rc = SQLITE_IOERR_WRITE;
- break;
}
}
}
@@ -742,7 +684,7 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
}else{
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
}
- }else{
+ }else if( (pGroup->flags & SQLITE_OPEN_MAIN_DB)==0 ){
int rc2;
int i;
sqlite3_file *pSubOpen;
@@ -789,7 +731,6 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
- int rc2;
int i;
multiplexEnter();
if( !pGroup->bEnabled ){
@@ -802,33 +743,32 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
}else{
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
*pSize = 0;
- for(i=0; 1; i++){
+ for(i=0; rc==SQLITE_OK; i++){
sqlite3_file *pSubOpen = 0;
int exists = 0;
rc = multiplexSubFilename(pGroup, i);
- if( rc ) break;
- rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z,
- SQLITE_ACCESS_EXISTS, &exists);
- if( rc2==SQLITE_OK && exists){
- /* if it exists, open it */
- pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
- }else{
- /* stop at first "gap" */
+ if( rc!=SQLITE_OK ) break;
+ if( pGroup->nReal>i && pGroup->aReal[i].p!=0 ){
+ exists = 1;
+ }else if( (pGroup->flags & SQLITE_OPEN_DELETEONCLOSE)==0 ){
+ const char *zReal = pGroup->aReal[i].z;
+ rc = pOrigVfs->xAccess(pOrigVfs, zReal, SQLITE_ACCESS_EXISTS, &exists);
+ }
+ if( exists==0 ){
+ /* stop at first "gap" or IO error. */
break;
}
- if( pSubOpen ){
- sqlite3_int64 sz;
- rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
- if( rc2!=SQLITE_OK ){
- rc = rc2;
- }else{
- if( sz>pGroup->szChunk ){
- rc = SQLITE_IOERR_FSTAT;
- }
- *pSize += sz;
+ if( rc==SQLITE_OK ){
+ pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
+ }
+ assert( pSubOpen || rc!=SQLITE_OK );
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 sz = 0;
+ rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( rc==SQLITE_OK && sz>pGroup->szChunk ){
+ rc = SQLITE_IOERR_FSTAT;
}
- }else{
- break;
+ *pSize += sz;
}
}
}
@@ -916,6 +856,9 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
if( pSubOpen ){
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
+ }
}
break;
}
@@ -928,7 +871,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
- if( pSubOpen ){
+ if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
return DEFAULT_SECTOR_SIZE;
@@ -1179,9 +1122,13 @@ static int test_multiplex_dump(
for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
pGroupTerm = Tcl_NewObj();
- pGroup->zName[pGroup->nName] = '\0';
- Tcl_ListObjAppendElement(interp, pGroupTerm,
+ if( pGroup->zName ){
+ pGroup->zName[pGroup->nName] = '\0';
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewStringObj(pGroup->zName, -1));
+ }else{
+ Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
+ }
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(pGroup->nName));
Tcl_ListObjAppendElement(interp, pGroupTerm,
diff --git a/src/test_osinst.c b/src/test_osinst.c
index 50d6250e6..b7f350577 100644
--- a/src/test_osinst.c
+++ b/src/test_osinst.c
@@ -389,7 +389,11 @@ static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
*/
static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
VfslogFile *p = (VfslogFile *)pFile;
- return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+ int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
+ }
+ return rc;
}
/*
diff --git a/src/test_vfstrace.c b/src/test_vfstrace.c
index 5e94f5cf0..62577207b 100644
--- a/src/test_vfstrace.c
+++ b/src/test_vfstrace.c
@@ -471,6 +471,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
}
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break;
+ case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
+ case SQLITE_FCNTL_PERSIST_WAL: zOp = "PERSIST_WAL"; break;
+ case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
+ case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
default: {
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
@@ -482,6 +486,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
pInfo->zVfsName, p->zFName, zOp);
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
+ pInfo->zVfsName, *(char**)pArg);
+ }
return rc;
}
diff --git a/src/trigger.c b/src/trigger.c
index 22c4877b6..3c4bf62a1 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -904,6 +904,7 @@ static TriggerPrg *codeRowTrigger(
}
pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab;
+ pProgram->nOnce = pSubParse->nOnce;
pProgram->token = (void *)pTrigger;
pPrg->aColmask[0] = pSubParse->oldmask;
pPrg->aColmask[1] = pSubParse->newmask;
diff --git a/src/update.c b/src/update.c
index 1e3052218..73d22690b 100644
--- a/src/update.c
+++ b/src/update.c
@@ -126,8 +126,8 @@ void sqlite3Update(
int regRowCount = 0; /* A count of rows changed */
int regOldRowid; /* The old rowid */
int regNewRowid; /* The new rowid */
- int regNew;
- int regOld = 0;
+ int regNew; /* Content of the NEW.* table in triggers */
+ int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
memset(&sContext, 0, sizeof(sContext));
@@ -276,6 +276,7 @@ void sqlite3Update(
#endif
/* Allocate required registers. */
+ regRowSet = ++pParse->nMem;
regOldRowid = regNewRowid = ++pParse->nMem;
if( pTrigger || hasFK ){
regOld = pParse->nMem + 1;
@@ -310,7 +311,7 @@ void sqlite3Update(
/* Begin the database scan
*/
- sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
pWInfo = sqlite3WhereBegin(
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
);
@@ -321,7 +322,6 @@ void sqlite3Update(
*/
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
if( !okOnePass ){
- regRowSet = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
}
@@ -425,9 +425,10 @@ void sqlite3Update(
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
}else{
j = aXRef[i];
if( j>=0 ){
diff --git a/src/vdbe.c b/src/vdbe.c
index e7fa05037..64ae54e3d 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -764,7 +764,8 @@ case OP_Goto: { /* jump */
** Write the current address onto register P1
** and then jump to address P2.
*/
-case OP_Gosub: { /* jump, in1 */
+case OP_Gosub: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=p->nMem );
pIn1 = &aMem[pOp->p1];
assert( (pIn1->flags & MEM_Dyn)==0 );
memAboutToChange(p, pIn1);
@@ -961,12 +962,25 @@ case OP_String: { /* out2-prerelease */
break;
}
-/* Opcode: Null * P2 * * *
+/* Opcode: Null * P2 P3 * *
**
-** Write a NULL into register P2.
+** Write a NULL into registers P2. If P3 greater than P2, then also write
+** NULL into register P3 and ever register in between P2 and P3. If P3
+** is less than P2 (typically P3 is zero) then only register P2 is
+** set to NULL
*/
case OP_Null: { /* out2-prerelease */
+ int cnt;
+ cnt = pOp->p3-pOp->p2;
+ assert( pOp->p3<=p->nMem );
pOut->flags = MEM_Null;
+ while( cnt>0 ){
+ pOut++;
+ memAboutToChange(p, pOut);
+ MemReleaseExt(pOut);
+ pOut->flags = MEM_Null;
+ cnt--;
+ }
break;
}
@@ -2023,27 +2037,33 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
/* Opcode: Once P1 P2 * * *
**
-** Jump to P2 if the value in register P1 is a not null or zero. If
-** the value is NULL or zero, fall through and change the P1 register
-** to an integer 1.
+** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
+** set the flag and fall through to the next instruction.
**
-** When P1 is not used otherwise in a program, this opcode falls through
-** once and jumps on all subsequent invocations. It is the equivalent
-** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
+** See also: JumpOnce
*/
+case OP_Once: { /* jump */
+ assert( pOp->p1<p->nOnceFlag );
+ if( p->aOnceFlag[pOp->p1] ){
+ pc = pOp->p2-1;
+ }else{
+ p->aOnceFlag[pOp->p1] = 1;
+ }
+ break;
+}
+
/* Opcode: If P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** in P1 is NULL then take the jump if P3 is non-zero.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is False. The value
-** is considered true if it has a numeric value of zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** is considered false if it has a numeric value of zero. If the value
+** in P1 is NULL then take the jump if P3 is zero.
*/
-case OP_Once: /* jump, in1 */
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
int c;
@@ -2060,12 +2080,6 @@ case OP_IfNot: { /* jump, in1 */
}
if( c ){
pc = pOp->p2-1;
- }else if( pOp->opcode==OP_Once ){
- assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
- memAboutToChange(p, pIn1);
- pIn1->flags = MEM_Int;
- pIn1->u.i = 1;
- REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@@ -5071,7 +5085,6 @@ case OP_Program: { /* jump */
pProgram = pOp->p4.pProgram;
pRt = &aMem[pOp->p3];
- assert( memIsValid(pRt) );
assert( pProgram->nOp>0 );
/* If the p5 flag is clear, then recursive invocation of triggers is
@@ -5110,7 +5123,8 @@ case OP_Program: { /* jump */
nMem = pProgram->nMem + pProgram->nCsr;
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
- + pProgram->nCsr * sizeof(VdbeCursor *);
+ + pProgram->nCsr * sizeof(VdbeCursor *)
+ + pProgram->nOnce * sizeof(u8);
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
@@ -5130,10 +5144,12 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
+ pFrame->aOnceFlag = p->aOnceFlag;
+ pFrame->nOnceFlag = p->nOnceFlag;
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
- pMem->flags = MEM_Null;
+ pMem->flags = MEM_Invalid;
pMem->db = db;
}
}else{
@@ -5155,7 +5171,11 @@ case OP_Program: { /* jump */
p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
+ p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
+ p->nOnceFlag = pProgram->nOnce;
+ p->nOp = pProgram->nOp;
pc = -1;
+ memset(p->aOnceFlag, 0, p->nOnceFlag);
break;
}
diff --git a/src/vdbe.h b/src/vdbe.h
index 948c73bca..90a43ce6e 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -82,6 +82,7 @@ struct SubProgram {
int nOp; /* Elements in aOp[] */
int nMem; /* Number of memory cells required */
int nCsr; /* Number of cursors required */
+ int nOnce; /* Number of OP_Once instructions */
void *token; /* id that may be used to recursive triggers */
SubProgram *pNext; /* Next sub-program already visited */
};
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 803ae1630..a56dedf6a 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -33,6 +33,9 @@ typedef unsigned char Bool;
/* Opaque type used by code in vdbesort.c */
typedef struct VdbeSorter VdbeSorter;
+/* Opaque type used by the explainer */
+typedef struct Explain Explain;
+
/*
** A cursor is a pointer into a single BTree within a database file.
** The cursor can seek to a BTree entry with a particular key, or
@@ -117,6 +120,8 @@ struct VdbeFrame {
int nOp; /* Size of aOp array */
Mem *aMem; /* Array of memory cells for parent frame */
int nMem; /* Number of entries in aMem */
+ u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
+ int nOnceFlag; /* Number of entries in aOnceFlag */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
@@ -256,6 +261,18 @@ struct sqlite3_context {
};
/*
+** An Explain object accumulates indented output which is helpful
+** in describing recursive data structures.
+*/
+struct Explain {
+ Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
+ StrAccum str; /* The string being accumulated */
+ int nIndent; /* Number of elements in aIndent */
+ u16 aIndent[100]; /* Levels of indentation */
+ char zBase[100]; /* Initial space */
+};
+
+/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
**
@@ -321,11 +338,17 @@ struct Vdbe {
#ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */
#endif
+#ifdef SQLITE_ENABLE_TREE_EXPLAIN
+ Explain *pExplain; /* The explainer */
+ char *zExplain; /* Explanation of data structures */
+#endif
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
+ int nOnceFlag; /* Size of array aOnceFlag[] */
+ u8 *aOnceFlag; /* Flags for OP_Once */
};
/*
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 7c69d28cf..747c846d0 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -913,13 +913,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
case P4_MEM: {
Mem *pMem = pOp->p4.pMem;
- assert( (pMem->flags & MEM_Null)==0 );
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+ }else if( pMem->flags & MEM_Null ){
+ sqlite3_snprintf(nTemp, zTemp, "NULL");
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -1094,7 +1095,7 @@ static void releaseMemArray(Mem *p, int N){
p->zMalloc = 0;
}
- p->flags = MEM_Null;
+ p->flags = MEM_Invalid;
}
db->mallocFailed = malloc_failed;
}
@@ -1469,6 +1470,7 @@ void sqlite3VdbeMakeReady(
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
+ int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
u8 *zCsr; /* Memory available for allocation */
u8 *zEnd; /* First byte past allocated memory */
@@ -1484,6 +1486,7 @@ void sqlite3VdbeMakeReady(
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
+ nOnce = pParse->nOnce;
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
@@ -1530,6 +1533,7 @@ void sqlite3VdbeMakeReady(
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
&zCsr, zEnd, &nByte);
+ p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
if( nByte ){
p->pFree = sqlite3DbMallocZero(db, nByte);
}
@@ -1538,6 +1542,7 @@ void sqlite3VdbeMakeReady(
}while( nByte && !db->mallocFailed );
p->nCursor = (u16)nCursor;
+ p->nOnceFlag = nOnce;
if( p->aVar ){
p->nVar = (ynVar)nVar;
for(n=0; n<nVar; n++){
@@ -1554,7 +1559,7 @@ void sqlite3VdbeMakeReady(
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Null;
+ p->aMem[n].flags = MEM_Invalid;
p->aMem[n].db = db;
}
}
@@ -1596,6 +1601,8 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
*/
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
+ v->aOnceFlag = pFrame->aOnceFlag;
+ v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -1658,8 +1665,10 @@ static void Cleanup(Vdbe *p){
/* Execute assert() statements to ensure that the Vdbe.apCsr[] and
** Vdbe.aMem[] arrays have already been cleaned up. */
int i;
- for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
- for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+ if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
+ if( p->aMem ){
+ for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
+ }
#endif
sqlite3DbFree(db, p->zErrMsg);
@@ -2127,6 +2136,7 @@ int sqlite3VdbeHalt(Vdbe *p){
if( p->db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
+ if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p);
if( p->magic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
@@ -2464,6 +2474,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ sqlite3DbFree(db, p->zExplain);
+ sqlite3DbFree(db, p->pExplain);
+#endif
sqlite3DbFree(db, p);
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index de123b550..4c4be1373 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -12,6 +12,8 @@
**
** This file contains code used to insert the values of host parameters
** (aka "wildcards") into the SQL text output by sqlite3_trace().
+**
+** The Vdbe parse-tree explainer is also found here.
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
@@ -152,3 +154,118 @@ char *sqlite3VdbeExpandSql(
}
#endif /* #ifndef SQLITE_OMIT_TRACE */
+
+/*****************************************************************************
+** The following code implements the data-structure explaining logic
+** for the Vdbe.
+*/
+
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+
+/*
+** Allocate a new Explain object
+*/
+void sqlite3ExplainBegin(Vdbe *pVdbe){
+ if( pVdbe ){
+ sqlite3BeginBenignMalloc();
+ Explain *p = sqlite3_malloc( sizeof(Explain) );
+ if( p ){
+ memset(p, 0, sizeof(*p));
+ p->pVdbe = pVdbe;
+ sqlite3_free(pVdbe->pExplain);
+ pVdbe->pExplain = p;
+ sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase),
+ SQLITE_MAX_LENGTH);
+ p->str.useMalloc = 2;
+ }else{
+ sqlite3EndBenignMalloc();
+ }
+ }
+}
+
+/*
+** Return true if the Explain ends with a new-line.
+*/
+static int endsWithNL(Explain *p){
+ return p && p->str.zText && p->str.nChar
+ && p->str.zText[p->str.nChar-1]=='\n';
+}
+
+/*
+** Append text to the indentation
+*/
+void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+ va_list ap;
+ if( p->nIndent && endsWithNL(p) ){
+ int n = p->nIndent;
+ if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent);
+ sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
+ }
+ va_start(ap, zFormat);
+ sqlite3VXPrintf(&p->str, 1, zFormat, ap);
+ va_end(ap);
+ }
+}
+
+/*
+** Append a '\n' if there is not already one.
+*/
+void sqlite3ExplainNL(Vdbe *pVdbe){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){
+ sqlite3StrAccumAppend(&p->str, "\n", 1);
+ }
+}
+
+/*
+** Push a new indentation level. Subsequent lines will be indented
+** so that they begin at the current cursor position.
+*/
+void sqlite3ExplainPush(Vdbe *pVdbe){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+ if( p->str.zText && p->nIndent<ArraySize(p->aIndent) ){
+ const char *z = p->str.zText;
+ int i = p->str.nChar-1;
+ int x;
+ while( i>=0 && z[i]!='\n' ){ i--; }
+ x = (p->str.nChar - 1) - i;
+ if( p->nIndent && x<p->aIndent[p->nIndent-1] ){
+ x = p->aIndent[p->nIndent-1];
+ }
+ p->aIndent[p->nIndent] = x;
+ }
+ p->nIndent++;
+ }
+}
+
+/*
+** Pop the indentation stack by one level.
+*/
+void sqlite3ExplainPop(Vdbe *p){
+ if( p && p->pExplain ) p->pExplain->nIndent--;
+}
+
+/*
+** Free the indentation structure
+*/
+void sqlite3ExplainFinish(Vdbe *pVdbe){
+ if( pVdbe && pVdbe->pExplain ){
+ sqlite3_free(pVdbe->zExplain);
+ sqlite3ExplainNL(pVdbe);
+ pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str);
+ sqlite3_free(pVdbe->pExplain);
+ pVdbe->pExplain = 0;
+ sqlite3EndBenignMalloc();
+ }
+}
+
+/*
+** Return the explanation of a virtual machine.
+*/
+const char *sqlite3VdbeExplanation(Vdbe *pVdbe){
+ return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0;
+}
+#endif /* defined(SQLITE_DEBUG) */
diff --git a/src/wal.c b/src/wal.c
index 2c905a29e..209c90975 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -1782,6 +1782,26 @@ static int walCheckpoint(
}
/*
+** Attempt to limit the WAL size to the size limit defined by
+** PRAGMA journal_size_limit.
+*/
+static void walLimitSize(Wal *pWal){
+ if( pWal->mxWalSize>=0 ){
+ i64 sz;
+ int rx;
+ sqlite3BeginBenignMalloc();
+ rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
+ if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
+ rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
+ }
+ sqlite3EndBenignMalloc();
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
+ }
+ }
+}
+
+/*
** Close a connection to a log file.
*/
int sqlite3WalClose(
@@ -1814,6 +1834,8 @@ int sqlite3WalClose(
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
if( rc==SQLITE_OK && bPersistWal!=1 ){
isDelete = 1;
+ }else{
+ walLimitSize(pWal);
}
}
@@ -2518,6 +2540,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
return rc;
}
+
/*
** This function is called just before writing a set of frames to the log
** file (see sqlite3WalFrames()). It checks to see if, instead of appending
@@ -2555,23 +2578,7 @@ static int walRestartLog(Wal *pWal){
int i; /* Loop counter */
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
- /* Limit the size of WAL file if the journal_size_limit PRAGMA is
- ** set to a non-negative value. Log errors encountered
- ** during the truncation attempt. */
- if( pWal->mxWalSize>=0 ){
- i64 sz;
- int rx;
- sqlite3BeginBenignMalloc();
- rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
- if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
- rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
- }
- sqlite3EndBenignMalloc();
- if( rx ){
- sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
- }
- }
-
+ walLimitSize(pWal);
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
diff --git a/src/where.c b/src/where.c
index a604d788c..78efbf979 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2005,7 +2005,6 @@ static void constructAutomaticIndex(
int nByte; /* Byte of memory needed for pIdx */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
- int regIsInit; /* Register set by initialization */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
KeyInfo *pKeyinfo; /* Key information for the index */
@@ -2022,8 +2021,7 @@ static void constructAutomaticIndex(
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
- regIsInit = ++pParse->nMem;
- addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
+ addrInit = sqlite3CodeOnce(pParse);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */