mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-31 06:21:07 +00:00
Snapshot of upstream SQLite 3.25.0
This commit is contained in:
+1211
-429
File diff suppressed because it is too large
Load Diff
+31
-6
@@ -234,6 +234,10 @@ static void openStatTable(
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q",
|
||||
pDb->zDbSName, zTab, zWhereType, zWhere
|
||||
);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
}else if( db->xPreUpdateCallback ){
|
||||
sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab);
|
||||
#endif
|
||||
}else{
|
||||
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
|
||||
@@ -481,6 +485,7 @@ static const FuncDef statInitFuncdef = {
|
||||
0, /* pNext */
|
||||
statInit, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
0, 0, /* xValue, xInverse */
|
||||
"stat_init", /* zName */
|
||||
{0}
|
||||
};
|
||||
@@ -797,6 +802,7 @@ static const FuncDef statPushFuncdef = {
|
||||
0, /* pNext */
|
||||
statPush, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
0, 0, /* xValue, xInverse */
|
||||
"stat_push", /* zName */
|
||||
{0}
|
||||
};
|
||||
@@ -948,6 +954,7 @@ static const FuncDef statGetFuncdef = {
|
||||
0, /* pNext */
|
||||
statGet, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
0, 0, /* xValue, xInverse */
|
||||
"stat_get", /* zName */
|
||||
{0}
|
||||
};
|
||||
@@ -998,6 +1005,9 @@ static void analyzeOneTable(
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
|
||||
int regPrev = iMem; /* MUST BE LAST (see below) */
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
Table *pStat1 = 0;
|
||||
#endif
|
||||
|
||||
pParse->nMem = MAX(pParse->nMem, iMem);
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
@@ -1008,7 +1018,7 @@ static void analyzeOneTable(
|
||||
/* Do not gather statistics on views or virtual tables */
|
||||
return;
|
||||
}
|
||||
if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){
|
||||
if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){
|
||||
/* Do not gather statistics on system tables */
|
||||
return;
|
||||
}
|
||||
@@ -1023,6 +1033,18 @@ static void analyzeOneTable(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( db->xPreUpdateCallback ){
|
||||
pStat1 = (Table*)sqlite3DbMallocZero(db, sizeof(Table) + 13);
|
||||
if( pStat1==0 ) return;
|
||||
pStat1->zName = (char*)&pStat1[1];
|
||||
memcpy(pStat1->zName, "sqlite_stat1", 13);
|
||||
pStat1->nCol = 3;
|
||||
pStat1->iPKey = -1;
|
||||
sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Establish a read-lock on the table at the shared-cache level.
|
||||
** Open a read-only cursor on the table. Also allocate a cursor number
|
||||
** to use for scanning indexes (iIdxCur). No index cursor is opened at
|
||||
@@ -1224,6 +1246,9 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
|
||||
#endif
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
|
||||
/* Add the entries to the stat3 or stat4 table. */
|
||||
@@ -1249,10 +1274,7 @@ static void analyzeOneTable(
|
||||
callStatGet(v, regStat4, STAT_GET_NLT, regLt);
|
||||
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
|
||||
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
||||
/* We know that the regSampleRowid row exists because it was read by
|
||||
** the previous loop. Thus the not-found jump of seekOp will never
|
||||
** be taken */
|
||||
VdbeCoverageNeverTaken(v);
|
||||
VdbeCoverage(v);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
|
||||
#else
|
||||
@@ -1287,6 +1309,9 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
|
||||
#endif
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
}
|
||||
}
|
||||
@@ -1889,7 +1914,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load the statistics from the sqlite_stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){
|
||||
if( rc==SQLITE_OK ){
|
||||
db->lookaside.bDisable++;
|
||||
rc = loadStat4(db, sInfo.zDatabase);
|
||||
db->lookaside.bDisable--;
|
||||
|
||||
+123
-81
@@ -55,6 +55,10 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
**
|
||||
** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the
|
||||
** third argument.
|
||||
**
|
||||
** If the db->init.reopenMemdb flags is set, then instead of attaching a
|
||||
** new database, close the database on db->init.iDb and reopen it as an
|
||||
** empty MemDB.
|
||||
*/
|
||||
static void attachFunc(
|
||||
sqlite3_context *context,
|
||||
@@ -75,70 +79,86 @@ static void attachFunc(
|
||||
sqlite3_vfs *pVfs;
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
zFile = (const char *)sqlite3_value_text(argv[0]);
|
||||
zName = (const char *)sqlite3_value_text(argv[1]);
|
||||
if( zFile==0 ) zFile = "";
|
||||
if( zName==0 ) zName = "";
|
||||
|
||||
/* Check for the following errors:
|
||||
**
|
||||
** * Too many attached databases,
|
||||
** * Transaction currently open
|
||||
** * Specified database name already being used.
|
||||
*/
|
||||
if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
|
||||
db->aLimit[SQLITE_LIMIT_ATTACHED]
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction");
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb)
|
||||
#else
|
||||
# define REOPEN_AS_MEMDB(db) (0)
|
||||
#endif
|
||||
|
||||
if( REOPEN_AS_MEMDB(db) ){
|
||||
/* This is not a real ATTACH. Instead, this routine is being called
|
||||
** from sqlite3_deserialize() to close database db->init.iDb and
|
||||
** reopen it as a MemDB */
|
||||
pVfs = sqlite3_vfs_find("memdb");
|
||||
if( pVfs==0 ) return;
|
||||
pNew = &db->aDb[db->init.iDb];
|
||||
if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt);
|
||||
pNew->pBt = 0;
|
||||
pNew->pSchema = 0;
|
||||
rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
|
||||
}else{
|
||||
/* This is a real ATTACH
|
||||
**
|
||||
** Check for the following errors:
|
||||
**
|
||||
** * Too many attached databases,
|
||||
** * Transaction currently open
|
||||
** * Specified database name already being used.
|
||||
*/
|
||||
if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
|
||||
db->aLimit[SQLITE_LIMIT_ATTACHED]
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
goto attach_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the new entry in the db->aDb[] array and initialize the schema
|
||||
** hash tables.
|
||||
*/
|
||||
if( db->aDb==db->aDbStatic ){
|
||||
aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
|
||||
if( aNew==0 ) return;
|
||||
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
||||
}else{
|
||||
aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
||||
if( aNew==0 ) return;
|
||||
}
|
||||
db->aDb = aNew;
|
||||
pNew = &db->aDb[db->nDb];
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
|
||||
/* Open the database file. If the btree is successfully opened, use
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialized.
|
||||
*/
|
||||
flags = db->openFlags;
|
||||
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
return;
|
||||
}
|
||||
assert( pVfs );
|
||||
flags |= SQLITE_OPEN_MAIN_DB;
|
||||
rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
|
||||
sqlite3_free( zPath );
|
||||
db->nDb++;
|
||||
}
|
||||
|
||||
/* Allocate the new entry in the db->aDb[] array and initialize the schema
|
||||
** hash tables.
|
||||
*/
|
||||
if( db->aDb==db->aDbStatic ){
|
||||
aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
|
||||
if( aNew==0 ) return;
|
||||
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
||||
}else{
|
||||
aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
||||
if( aNew==0 ) return;
|
||||
}
|
||||
db->aDb = aNew;
|
||||
pNew = &db->aDb[db->nDb];
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
|
||||
/* Open the database file. If the btree is successfully opened, use
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialized.
|
||||
*/
|
||||
flags = db->openFlags;
|
||||
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
return;
|
||||
}
|
||||
assert( pVfs );
|
||||
flags |= SQLITE_OPEN_MAIN_DB;
|
||||
rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
|
||||
sqlite3_free( zPath );
|
||||
db->nDb++;
|
||||
db->skipBtreeMutex = 0;
|
||||
db->noSharedCache = 0;
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
rc = SQLITE_ERROR;
|
||||
zErrDyn = sqlite3MPrintf(db, "database is already attached");
|
||||
@@ -164,7 +184,7 @@ static void attachFunc(
|
||||
sqlite3BtreeLeave(pNew->pBt);
|
||||
}
|
||||
pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
pNew->zDbSName = sqlite3DbStrDup(db, zName);
|
||||
if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && pNew->zDbSName==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
@@ -204,13 +224,16 @@ static void attachFunc(
|
||||
|
||||
/* If the file was opened successfully, read the schema for the new database.
|
||||
** If this fails, or if opening the file failed, then close the file and
|
||||
** remove the entry from the db->aDb[] array. i.e. put everything back the way
|
||||
** we found it.
|
||||
** remove the entry from the db->aDb[] array. i.e. put everything back the
|
||||
** way we found it.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3BtreeEnterAll(db);
|
||||
db->init.iDb = 0;
|
||||
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
|
||||
rc = sqlite3Init(db, &zErrDyn);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
assert( zErrDyn==0 || rc!=SQLITE_OK );
|
||||
}
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -222,21 +245,23 @@ static void attachFunc(
|
||||
}
|
||||
#endif
|
||||
if( rc ){
|
||||
int iDb = db->nDb - 1;
|
||||
assert( iDb>=2 );
|
||||
if( db->aDb[iDb].pBt ){
|
||||
sqlite3BtreeClose(db->aDb[iDb].pBt);
|
||||
db->aDb[iDb].pBt = 0;
|
||||
db->aDb[iDb].pSchema = 0;
|
||||
}
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->nDb = iDb;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
sqlite3DbFree(db, zErrDyn);
|
||||
zErrDyn = sqlite3MPrintf(db, "out of memory");
|
||||
}else if( zErrDyn==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile);
|
||||
if( !REOPEN_AS_MEMDB(db) ){
|
||||
int iDb = db->nDb - 1;
|
||||
assert( iDb>=2 );
|
||||
if( db->aDb[iDb].pBt ){
|
||||
sqlite3BtreeClose(db->aDb[iDb].pBt);
|
||||
db->aDb[iDb].pBt = 0;
|
||||
db->aDb[iDb].pSchema = 0;
|
||||
}
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->nDb = iDb;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
sqlite3DbFree(db, zErrDyn);
|
||||
zErrDyn = sqlite3MPrintf(db, "out of memory");
|
||||
}else if( zErrDyn==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile);
|
||||
}
|
||||
}
|
||||
goto attach_error;
|
||||
}
|
||||
@@ -288,11 +313,6 @@ static void detachFunc(
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr,
|
||||
"cannot DETACH database within transaction");
|
||||
goto detach_error;
|
||||
}
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
||||
goto detach_error;
|
||||
@@ -394,6 +414,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
0, /* pNext */
|
||||
detachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
0, 0, /* xValue, xInverse */
|
||||
"sqlite_detach", /* zName */
|
||||
{0}
|
||||
};
|
||||
@@ -413,6 +434,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
0, /* pNext */
|
||||
attachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
0, 0, /* xValue, xInverse */
|
||||
"sqlite_attach", /* zName */
|
||||
{0}
|
||||
};
|
||||
@@ -483,6 +505,9 @@ int sqlite3FixSrcList(
|
||||
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
||||
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
|
||||
#endif
|
||||
if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -513,8 +538,13 @@ int sqlite3FixSelect(
|
||||
if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
|
||||
return 1;
|
||||
if( pSelect->pWith ){
|
||||
int i;
|
||||
for(i=0; i<pSelect->pWith->nCte; i++){
|
||||
if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
@@ -577,6 +607,18 @@ int sqlite3FixTriggerStep(
|
||||
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUp = pStep->pUpsert;
|
||||
if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
|
||||
|| sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
|
||||
|| sqlite3FixExprList(pFix, pUp->pUpsertSet)
|
||||
|| sqlite3FixExpr(pFix, pUp->pUpsertWhere)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pStep = pStep->pNext;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+6
-8
@@ -78,7 +78,7 @@ int sqlite3_set_authorizer(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xAuth = (sqlite3_xauth)xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -118,11 +118,9 @@ int sqlite3AuthReadCol(
|
||||
#endif
|
||||
);
|
||||
if( rc==SQLITE_DENY ){
|
||||
if( db->nDb>2 || iDb!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol);
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol);
|
||||
}
|
||||
char *z = sqlite3_mprintf("%s.%s", zTab, zCol);
|
||||
if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z);
|
||||
sqlite3ErrorMsg(pParse, "access to %z is prohibited", z);
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse);
|
||||
@@ -152,6 +150,7 @@ void sqlite3AuthRead(
|
||||
int iDb; /* The index of the database the expression refers to */
|
||||
int iCol; /* Index of column in table */
|
||||
|
||||
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
||||
if( db->xAuth==0 ) return;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
|
||||
if( iDb<0 ){
|
||||
@@ -160,7 +159,6 @@ void sqlite3AuthRead(
|
||||
return;
|
||||
}
|
||||
|
||||
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
||||
if( pExpr->op==TK_TRIGGER ){
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else{
|
||||
@@ -209,7 +207,7 @@ int sqlite3AuthCheck(
|
||||
/* Don't do any authorization checks if the database is initialising
|
||||
** or if the parser is being invoked from within sqlite3_declare_vtab.
|
||||
*/
|
||||
if( db->init.busy || IN_DECLARE_VTAB ){
|
||||
if( db->init.busy || IN_SPECIAL_PARSE ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -382,7 +382,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
** before this function exits.
|
||||
*/
|
||||
if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
|
||||
rc = sqlite3BtreeBeginTrans(p->pSrc, 0);
|
||||
rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
@@ -398,10 +398,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2,
|
||||
(int*)&p->iDestSchema))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* Do not allow backup if the destination database is in WAL mode
|
||||
|
||||
+3
-3
@@ -195,10 +195,10 @@ static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
|
||||
skipOk = 0;
|
||||
}
|
||||
}
|
||||
db->skipBtreeMutex = skipOk;
|
||||
db->noSharedCache = skipOk;
|
||||
}
|
||||
void sqlite3BtreeEnterAll(sqlite3 *db){
|
||||
if( db->skipBtreeMutex==0 ) btreeEnterAll(db);
|
||||
if( db->noSharedCache==0 ) btreeEnterAll(db);
|
||||
}
|
||||
static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){
|
||||
int i;
|
||||
@@ -210,7 +210,7 @@ static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){
|
||||
}
|
||||
}
|
||||
void sqlite3BtreeLeaveAll(sqlite3 *db){
|
||||
if( db->skipBtreeMutex==0 ) btreeLeaveAll(db);
|
||||
if( db->noSharedCache==0 ) btreeLeaveAll(db);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
+616
-296
File diff suppressed because it is too large
Load Diff
+26
-4
@@ -78,7 +78,7 @@ int sqlite3BtreeGetOptimalReserve(Btree*);
|
||||
int sqlite3BtreeGetReserveNoMutex(Btree *p);
|
||||
int sqlite3BtreeSetAutoVacuum(Btree *, int);
|
||||
int sqlite3BtreeGetAutoVacuum(Btree *);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int,int*);
|
||||
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
@@ -230,6 +230,7 @@ int sqlite3BtreeCursor(
|
||||
struct KeyInfo*, /* First argument to compare function */
|
||||
BtCursor *pCursor /* Space to write cursor structure */
|
||||
);
|
||||
BtCursor *sqlite3BtreeFakeValidCursor(void);
|
||||
int sqlite3BtreeCursorSize(void);
|
||||
void sqlite3BtreeCursorZero(BtCursor*);
|
||||
void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
|
||||
@@ -258,13 +259,28 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
** entry in either an index or table btree.
|
||||
**
|
||||
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
|
||||
** an arbitrary key and no data. These btrees have pKey,nKey set to their
|
||||
** key and pData,nData,nZero set to zero.
|
||||
** an arbitrary key and no data. These btrees have pKey,nKey set to the
|
||||
** key and the pData,nData,nZero fields are uninitialized. The aMem,nMem
|
||||
** fields give an array of Mem objects that are a decomposition of the key.
|
||||
** The nMem field might be zero, indicating that no decomposition is available.
|
||||
**
|
||||
** Table btrees (used for rowid tables) contain an integer rowid used as
|
||||
** the key and passed in the nKey field. The pKey field is zero.
|
||||
** pData,nData hold the content of the new entry. nZero extra zero bytes
|
||||
** are appended to the end of the content when constructing the entry.
|
||||
** The aMem,nMem fields are uninitialized for table btrees.
|
||||
**
|
||||
** Field usage summary:
|
||||
**
|
||||
** Table BTrees Index Btrees
|
||||
**
|
||||
** pKey always NULL encoded key
|
||||
** nKey the ROWID length of pKey
|
||||
** pData data not used
|
||||
** aMem not used decomposed key value
|
||||
** nMem not used entries in aMem
|
||||
** nData length of pData not used
|
||||
** nZero extra zeros after pData not used
|
||||
**
|
||||
** This object is used to pass information into sqlite3BtreeInsert(). The
|
||||
** same information used to be passed as five separate parameters. But placing
|
||||
@@ -275,7 +291,7 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
struct BtreePayload {
|
||||
const void *pKey; /* Key content for indexes. NULL for tables */
|
||||
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
|
||||
const void *pData; /* Data for tables. NULL for indexes */
|
||||
const void *pData; /* Data for tables. */
|
||||
sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */
|
||||
u16 nMem; /* Number of aMem[] value. Might be zero */
|
||||
int nData; /* Size of pData. 0 if none. */
|
||||
@@ -285,11 +301,17 @@ struct BtreePayload {
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int flags, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
void sqlite3BtreeSkipNext(BtCursor*);
|
||||
#endif
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int flags);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int flags);
|
||||
i64 sqlite3BtreeIntegerKey(BtCursor*);
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
i64 sqlite3BtreeOffset(BtCursor*);
|
||||
#endif
|
||||
int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*);
|
||||
const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
||||
u32 sqlite3BtreePayloadSize(BtCursor*);
|
||||
|
||||
+16
-15
@@ -499,30 +499,31 @@ struct CellInfo {
|
||||
** eState==FAULT: Cursor fault with skipNext as error code.
|
||||
*/
|
||||
struct BtCursor {
|
||||
Btree *pBtree; /* The Btree to which this cursor belongs */
|
||||
BtShared *pBt; /* The BtShared this cursor points to */
|
||||
BtCursor *pNext; /* Forms a linked list of all cursors */
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
void *pKey; /* Saved key that was cursor last known position */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
int nOvflAlloc; /* Allocated size of aOverflow[] array */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
|
||||
** Error code if eState==CURSOR_FAULT */
|
||||
u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
u8 curFlags; /* zero or more BTCF_* flags defined below */
|
||||
u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */
|
||||
u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
u8 hints; /* As configured by CursorSetHints() */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
|
||||
** Error code if eState==CURSOR_FAULT */
|
||||
Btree *pBtree; /* The Btree to which this cursor belongs */
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
void *pKey; /* Saved key that was cursor last known position */
|
||||
/* All fields above are zeroed when the cursor is allocated. See
|
||||
** sqlite3BtreeCursorZero(). Fields that follow must be manually
|
||||
** initialized. */
|
||||
#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */
|
||||
BtShared *pBt; /* The BtShared this cursor points to */
|
||||
BtCursor *pNext; /* Forms a linked list of all cursors */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
i8 iPage; /* Index of current page in apPage */
|
||||
u8 curIntKey; /* Value of apPage[0]->intKey */
|
||||
u16 ix; /* Current index for apPage[iPage] */
|
||||
u16 aiIdx[BTCURSOR_MAX_DEPTH-1]; /* Current index in apPage[i] */
|
||||
struct KeyInfo *pKeyInfo; /* Arg passed to comparison function */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
MemPage *pPage; /* Current page */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH-1]; /* Stack of parents of current page */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -565,8 +566,8 @@ struct BtCursor {
|
||||
** Do nothing else with this cursor. Any attempt to use the cursor
|
||||
** should return the error code stored in BtCursor.skipNext
|
||||
*/
|
||||
#define CURSOR_INVALID 0
|
||||
#define CURSOR_VALID 1
|
||||
#define CURSOR_VALID 0
|
||||
#define CURSOR_INVALID 1
|
||||
#define CURSOR_SKIPNEXT 2
|
||||
#define CURSOR_REQUIRESEEK 3
|
||||
#define CURSOR_FAULT 4
|
||||
|
||||
+347
-212
File diff suppressed because it is too large
Load Diff
+6
-3
@@ -105,6 +105,7 @@ CollSeq *sqlite3GetCollSeq(
|
||||
assert( !p || p->xCmp );
|
||||
if( p==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -374,7 +375,7 @@ FuncDef *sqlite3FindFunction(
|
||||
|
||||
/* If no match is found, search the built-in functions.
|
||||
**
|
||||
** If the SQLITE_PreferBuiltin flag is set, then search the built-in
|
||||
** If the DBFLAG_PreferBuiltin flag is set, then search the built-in
|
||||
** functions even if a prior app-defined function was found. And give
|
||||
** priority to built-in functions.
|
||||
**
|
||||
@@ -384,7 +385,7 @@ FuncDef *sqlite3FindFunction(
|
||||
** new function. But the FuncDefs for built-in functions are read-only.
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
*/
|
||||
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
|
||||
if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
|
||||
bestScore = 0;
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
|
||||
p = functionSearch(h, zName);
|
||||
@@ -405,10 +406,12 @@ FuncDef *sqlite3FindFunction(
|
||||
if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
|
||||
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
||||
FuncDef *pOther;
|
||||
u8 *z;
|
||||
pBest->zName = (const char*)&pBest[1];
|
||||
pBest->nArg = (u16)nArg;
|
||||
pBest->funcFlags = enc;
|
||||
memcpy((char*)&pBest[1], zName, nName+1);
|
||||
for(z=(u8*)pBest->zName; *z; z++) *z = sqlite3UpperToLower[*z];
|
||||
pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
|
||||
if( pOther==pBest ){
|
||||
sqlite3DbFree(db, pBest);
|
||||
@@ -457,8 +460,8 @@ void sqlite3SchemaClear(void *p){
|
||||
pSchema->pSeqTab = 0;
|
||||
if( pSchema->schemaFlags & DB_SchemaLoaded ){
|
||||
pSchema->iGeneration++;
|
||||
pSchema->schemaFlags &= ~DB_SchemaLoaded;
|
||||
}
|
||||
pSchema->schemaFlags &= ~(DB_SchemaLoaded|DB_ResetWanted);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+14
-2
@@ -30,6 +30,12 @@
|
||||
#define CTIMEOPT_VAL_(opt) #opt
|
||||
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
|
||||
|
||||
/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This
|
||||
** option requires a separate macro because legal values contain a single
|
||||
** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */
|
||||
#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2
|
||||
#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt)
|
||||
|
||||
/*
|
||||
** An array of names of all compile-time options. This array should
|
||||
** be sorted A-Z.
|
||||
@@ -113,7 +119,7 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
"DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
|
||||
#endif
|
||||
#ifdef SQLITE_DEFAULT_LOOKASIDE
|
||||
"DEFAULT_LOOKASIDE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOOKASIDE),
|
||||
"DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE),
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_MEMSTATUS
|
||||
"DEFAULT_MEMSTATUS",
|
||||
@@ -184,8 +190,11 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_ATOMIC_WRITE
|
||||
"ENABLE_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
"ENABLE_BATCH_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_CEROD
|
||||
"ENABLE_CEROD",
|
||||
"ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
|
||||
#endif
|
||||
#if SQLITE_ENABLE_COLUMN_METADATA
|
||||
"ENABLE_COLUMN_METADATA",
|
||||
@@ -283,6 +292,9 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_SNAPSHOT
|
||||
"ENABLE_SNAPSHOT",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_SORTER_REFERENCES
|
||||
"ENABLE_SORTER_REFERENCES",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_SQLLOG
|
||||
"ENABLE_SQLLOG",
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@
|
||||
**
|
||||
** Jean Meeus
|
||||
** Astronomical Algorithms, 2nd Edition, 1998
|
||||
** ISBM 0-943396-61-1
|
||||
** ISBN 0-943396-61-1
|
||||
** Willmann-Bell, Inc
|
||||
** Richmond, Virginia (USA)
|
||||
*/
|
||||
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
** 2017-10-11
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains an implementation of the "sqlite_dbpage" virtual table.
|
||||
**
|
||||
** The sqlite_dbpage virtual table is used to read or write whole raw
|
||||
** pages of the database file. The pager interface is used so that
|
||||
** uncommitted changes and changes recorded in the WAL file are correctly
|
||||
** retrieved.
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** SELECT data FROM sqlite_dbpage('aux1') WHERE pgno=123;
|
||||
**
|
||||
** This is an eponymous virtual table so it does not need to be created before
|
||||
** use. The optional argument to the sqlite_dbpage() table name is the
|
||||
** schema for the database file that is to be read. The default schema is
|
||||
** "main".
|
||||
**
|
||||
** The data field of sqlite_dbpage table can be updated. The new
|
||||
** value must be a BLOB which is the correct page size, otherwise the
|
||||
** update fails. Rows may not be deleted or inserted.
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h" /* Requires access to internal data structures */
|
||||
#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
|
||||
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
|
||||
typedef struct DbpageTable DbpageTable;
|
||||
typedef struct DbpageCursor DbpageCursor;
|
||||
|
||||
struct DbpageCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
int pgno; /* Current page number */
|
||||
int mxPgno; /* Last page to visit on this scan */
|
||||
Pager *pPager; /* Pager being read/written */
|
||||
DbPage *pPage1; /* Page 1 of the database */
|
||||
int iDb; /* Index of database to analyze */
|
||||
int szPage; /* Size of each page in bytes */
|
||||
};
|
||||
|
||||
struct DbpageTable {
|
||||
sqlite3_vtab base; /* Base class. Must be first */
|
||||
sqlite3 *db; /* The database */
|
||||
};
|
||||
|
||||
/* Columns */
|
||||
#define DBPAGE_COLUMN_PGNO 0
|
||||
#define DBPAGE_COLUMN_DATA 1
|
||||
#define DBPAGE_COLUMN_SCHEMA 2
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Connect to or create a dbpagevfs virtual table.
|
||||
*/
|
||||
static int dbpageConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
DbpageTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
assert( rc==SQLITE_OK || pTab==0 );
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(pTab, 0, sizeof(DbpageTable));
|
||||
pTab->db = db;
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy a dbpagevfs virtual table.
|
||||
*/
|
||||
static int dbpageDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** idxNum:
|
||||
**
|
||||
** 0 schema=main, full table scan
|
||||
** 1 schema=main, pgno=?1
|
||||
** 2 schema=?1, full table scan
|
||||
** 3 schema=?1, pgno=?2
|
||||
*/
|
||||
static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int i;
|
||||
int iPlan = 0;
|
||||
|
||||
/* If there is a schema= constraint, it must be honored. Report a
|
||||
** ridiculously large estimated cost if the schema= constraint is
|
||||
** unavailable
|
||||
*/
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
|
||||
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
if( !p->usable ){
|
||||
/* No solution. Use the default SQLITE_BIG_DBL cost */
|
||||
pIdxInfo->estimatedRows = 0x7fffffff;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iPlan = 2;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we reach this point, it means that either there is no schema=
|
||||
** constraint (in which case we use the "main" schema) or else the
|
||||
** schema constraint was accepted. Lower the estimated cost accordingly
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 1.0e6;
|
||||
|
||||
/* Check for constraints against pgno */
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
pIdxInfo->estimatedRows = 1;
|
||||
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
iPlan |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pIdxInfo->idxNum = iPlan;
|
||||
|
||||
if( pIdxInfo->nOrderBy>=1
|
||||
&& pIdxInfo->aOrderBy[0].iColumn<=0
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new dbpagevfs cursor.
|
||||
*/
|
||||
static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
DbpageCursor *pCsr;
|
||||
|
||||
pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
memset(pCsr, 0, sizeof(DbpageCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
pCsr->pgno = -1;
|
||||
}
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a dbpagevfs cursor.
|
||||
*/
|
||||
static int dbpageClose(sqlite3_vtab_cursor *pCursor){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a dbpagevfs cursor to the next entry in the file.
|
||||
*/
|
||||
static int dbpageNext(sqlite3_vtab_cursor *pCursor){
|
||||
int rc = SQLITE_OK;
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
pCsr->pgno++;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dbpageEof(sqlite3_vtab_cursor *pCursor){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
return pCsr->pgno > pCsr->mxPgno;
|
||||
}
|
||||
|
||||
/*
|
||||
** idxNum:
|
||||
**
|
||||
** 0 schema=main, full table scan
|
||||
** 1 schema=main, pgno=?1
|
||||
** 2 schema=?1, full table scan
|
||||
** 3 schema=?1, pgno=?2
|
||||
**
|
||||
** idxStr is not used
|
||||
*/
|
||||
static int dbpageFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
|
||||
int rc;
|
||||
sqlite3 *db = pTab->db;
|
||||
Btree *pBt;
|
||||
|
||||
/* Default setting is no rows of result */
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = 0;
|
||||
|
||||
if( idxNum & 2 ){
|
||||
const char *zSchema;
|
||||
assert( argc>=1 );
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
pCsr->iDb = sqlite3FindDbName(db, zSchema);
|
||||
if( pCsr->iDb<0 ) return SQLITE_OK;
|
||||
}else{
|
||||
pCsr->iDb = 0;
|
||||
}
|
||||
pBt = db->aDb[pCsr->iDb].pBt;
|
||||
if( pBt==0 ) return SQLITE_OK;
|
||||
pCsr->pPager = sqlite3BtreePager(pBt);
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
|
||||
if( idxNum & 1 ){
|
||||
assert( argc>(idxNum>>1) );
|
||||
pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
|
||||
if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = 0;
|
||||
}else{
|
||||
pCsr->mxPgno = pCsr->pgno;
|
||||
}
|
||||
}else{
|
||||
assert( pCsr->pgno==1 );
|
||||
}
|
||||
if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
|
||||
rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dbpageColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
int rc = SQLITE_OK;
|
||||
switch( i ){
|
||||
case 0: { /* pgno */
|
||||
sqlite3_result_int(ctx, pCsr->pgno);
|
||||
break;
|
||||
}
|
||||
case 1: { /* data */
|
||||
DbPage *pDbPage = 0;
|
||||
rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
break;
|
||||
}
|
||||
default: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
*pRowid = pCsr->pgno;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int dbpageUpdate(
|
||||
sqlite3_vtab *pVtab,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
DbpageTable *pTab = (DbpageTable *)pVtab;
|
||||
Pgno pgno;
|
||||
DbPage *pDbPage = 0;
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
const char *zSchema;
|
||||
int iDb;
|
||||
Btree *pBt;
|
||||
Pager *pPager;
|
||||
int szPage;
|
||||
|
||||
if( argc==1 ){
|
||||
zErr = "cannot delete";
|
||||
goto update_fail;
|
||||
}
|
||||
pgno = sqlite3_value_int(argv[0]);
|
||||
if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
|
||||
zErr = "cannot insert";
|
||||
goto update_fail;
|
||||
}
|
||||
zSchema = (const char*)sqlite3_value_text(argv[4]);
|
||||
iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
|
||||
if( iDb<0 ){
|
||||
zErr = "no such schema";
|
||||
goto update_fail;
|
||||
}
|
||||
pBt = pTab->db->aDb[iDb].pBt;
|
||||
if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){
|
||||
zErr = "bad page number";
|
||||
goto update_fail;
|
||||
}
|
||||
szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|
||||
|| sqlite3_value_bytes(argv[3])!=szPage
|
||||
){
|
||||
zErr = "bad page value";
|
||||
goto update_fail;
|
||||
}
|
||||
pPager = sqlite3BtreePager(pBt);
|
||||
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerWrite(pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(sqlite3PagerGetData(pDbPage),
|
||||
sqlite3_value_blob(argv[3]),
|
||||
szPage);
|
||||
}
|
||||
}
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
return rc;
|
||||
|
||||
update_fail:
|
||||
sqlite3_free(pVtab->zErrMsg);
|
||||
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Since we do not know in advance which database files will be
|
||||
** written by the sqlite_dbpage virtual table, start a write transaction
|
||||
** on them all.
|
||||
*/
|
||||
static int dbpageBegin(sqlite3_vtab *pVtab){
|
||||
DbpageTable *pTab = (DbpageTable *)pVtab;
|
||||
sqlite3 *db = pTab->db;
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the "dbpage" virtual table module
|
||||
*/
|
||||
int sqlite3DbpageRegister(sqlite3 *db){
|
||||
static sqlite3_module dbpage_module = {
|
||||
0, /* iVersion */
|
||||
dbpageConnect, /* xCreate */
|
||||
dbpageConnect, /* xConnect */
|
||||
dbpageBestIndex, /* xBestIndex */
|
||||
dbpageDisconnect, /* xDisconnect */
|
||||
dbpageDisconnect, /* xDestroy */
|
||||
dbpageOpen, /* xOpen - open a cursor */
|
||||
dbpageClose, /* xClose - close a cursor */
|
||||
dbpageFilter, /* xFilter - configure scan constraints */
|
||||
dbpageNext, /* xNext - advance a cursor */
|
||||
dbpageEof, /* xEof - check for end of scan */
|
||||
dbpageColumn, /* xColumn - read data */
|
||||
dbpageRowid, /* xRowid - read data */
|
||||
dbpageUpdate, /* xUpdate */
|
||||
dbpageBegin, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
};
|
||||
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
|
||||
}
|
||||
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
|
||||
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
|
||||
+1
-1
@@ -424,7 +424,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
|
||||
*/
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
x[0] = pCsr->iPageno;
|
||||
if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
pCsr->iOffset = x[0];
|
||||
pCsr->szPage = (int)x[1];
|
||||
}
|
||||
|
||||
+91
-49
@@ -90,6 +90,8 @@ void sqlite3MaterializeView(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pView, /* View definition */
|
||||
Expr *pWhere, /* Optional WHERE clause to be added */
|
||||
ExprList *pOrderBy, /* Optional ORDER BY clause */
|
||||
Expr *pLimit, /* Optional LIMIT clause */
|
||||
int iCur /* Cursor number for ephemeral table */
|
||||
){
|
||||
SelectDest dest;
|
||||
@@ -106,8 +108,8 @@ void sqlite3MaterializeView(
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0,
|
||||
SF_IncludeHidden, 0, 0);
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
|
||||
SF_IncludeHidden, pLimit);
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
||||
sqlite3Select(pParse, pSel, &dest);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
@@ -129,29 +131,29 @@ Expr *sqlite3LimitWhere(
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
||||
Expr *pLimit, /* The LIMIT clause. May be null */
|
||||
Expr *pOffset, /* The OFFSET clause. May be null */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
||||
){
|
||||
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
|
||||
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
|
||||
Expr *pSelectRowid = NULL; /* SELECT rowid ... */
|
||||
ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */
|
||||
SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */
|
||||
Select *pSelect = NULL; /* Complete SELECT tree */
|
||||
Table *pTab;
|
||||
|
||||
/* Check that there isn't an ORDER BY without a LIMIT clause.
|
||||
*/
|
||||
if( pOrderBy && (pLimit == 0) ) {
|
||||
if( pOrderBy && pLimit==0 ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
goto limit_where_cleanup;
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only need to generate a select expression if there
|
||||
** is a limit/offset term to enforce.
|
||||
*/
|
||||
if( pLimit == 0 ) {
|
||||
/* if pLimit is null, pOffset will always be null as well. */
|
||||
assert( pOffset == 0 );
|
||||
return pWhere;
|
||||
}
|
||||
|
||||
@@ -164,36 +166,47 @@ Expr *sqlite3LimitWhere(
|
||||
** );
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup;
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
|
||||
if( pEList == 0 ) goto limit_where_cleanup;
|
||||
pTab = pSrc->a[0].pTab;
|
||||
if( HasRowid(pTab) ){
|
||||
pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pEList = sqlite3ExprListAppend(
|
||||
pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0)
|
||||
);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
if( pPk->nKeyCol==1 ){
|
||||
const char *zName = pTab->aCol[pPk->aiColumn[0]].zName;
|
||||
pLhs = sqlite3Expr(db, TK_ID, zName);
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));
|
||||
}else{
|
||||
int i;
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName);
|
||||
pEList = sqlite3ExprListAppend(pParse, pEList, p);
|
||||
}
|
||||
pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( pLhs ){
|
||||
pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSrc->a[0].pTab = 0;
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup;
|
||||
}
|
||||
pSrc->a[0].pTab = pTab;
|
||||
pSrc->a[0].pIBIndex = 0;
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
|
||||
pOrderBy,0,pLimit,pOffset);
|
||||
if( pSelect == 0 ) return 0;
|
||||
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
|
||||
pOrderBy,0,pLimit
|
||||
);
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0) : 0;
|
||||
pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0);
|
||||
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
|
||||
return pInClause;
|
||||
|
||||
limit_where_cleanup:
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse->db, pLimit);
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
|
||||
/* && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
@@ -208,7 +221,9 @@ limit_where_cleanup:
|
||||
void sqlite3DeleteFrom(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* The table from which we should delete things */
|
||||
Expr *pWhere /* The WHERE clause. May be null */
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
ExprList *pOrderBy, /* ORDER BY clause. May be null */
|
||||
Expr *pLimit /* LIMIT clause. May be null */
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
@@ -223,7 +238,7 @@ void sqlite3DeleteFrom(
|
||||
AuthContext sContext; /* Authorization context */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
int iDb; /* Database number */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
int memCnt = 0; /* Memory cell used for change counting */
|
||||
int rcauth; /* Value returned by authorization callback */
|
||||
int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
@@ -253,6 +268,7 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
|
||||
/* Locate the table which we want to delete. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
@@ -267,16 +283,26 @@ void sqlite3DeleteFrom(
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
#else
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
#endif
|
||||
bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
# define isView 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
if( !isView ){
|
||||
pWhere = sqlite3LimitWhere(
|
||||
pParse, pTabList, pWhere, pOrderBy, pLimit, "DELETE"
|
||||
);
|
||||
pOrderBy = 0;
|
||||
pLimit = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
@@ -317,15 +343,19 @@ void sqlite3DeleteFrom(
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3BeginWriteOperation(pParse, bComplex, iDb);
|
||||
|
||||
/* If we are trying to delete from a view, realize that view into
|
||||
** an ephemeral table.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
|
||||
sqlite3MaterializeView(pParse, pTab,
|
||||
pWhere, pOrderBy, pLimit, iTabCur
|
||||
);
|
||||
iDataCur = iIdxCur = iTabCur;
|
||||
pOrderBy = 0;
|
||||
pLimit = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -341,7 +371,10 @@ void sqlite3DeleteFrom(
|
||||
/* Initialize the counter of the number of rows deleted, if
|
||||
** we are counting rows.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
if( (db->flags & SQLITE_CountRows)!=0
|
||||
&& !pParse->nested
|
||||
&& !pParse->pTriggerTab
|
||||
){
|
||||
memCnt = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
||||
}
|
||||
@@ -369,7 +402,7 @@ void sqlite3DeleteFrom(
|
||||
assert( !isView );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt ? memCnt : -1,
|
||||
pTab->zName, P4_STATIC);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
@@ -414,9 +447,10 @@ void sqlite3DeleteFrom(
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
|
||||
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
|
||||
if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);
|
||||
|
||||
/* Keep track of the number of rows to be deleted */
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
if( memCnt ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
}
|
||||
|
||||
@@ -429,9 +463,8 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
iKey = iPk;
|
||||
}else{
|
||||
iKey = pParse->nMem + 1;
|
||||
iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
|
||||
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
||||
iKey = ++pParse->nMem;
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, -1, iKey);
|
||||
}
|
||||
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
@@ -502,7 +535,11 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
}else if( pPk ){
|
||||
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
||||
}
|
||||
assert( nKey==0 ); /* OP_Found will use a composite key */
|
||||
}else{
|
||||
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
|
||||
@@ -515,13 +552,16 @@ void sqlite3DeleteFrom(
|
||||
if( IsVirtual(pTab) ){
|
||||
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
||||
sqlite3MayAbort(pParse);
|
||||
if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
|
||||
pParse->isMultiWrite = 0;
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iTabCur);
|
||||
if( sqlite3IsToplevel(pParse) ){
|
||||
pParse->isMultiWrite = 0;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
@@ -555,7 +595,7 @@ void sqlite3DeleteFrom(
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
|
||||
if( memCnt ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
||||
@@ -565,6 +605,10 @@ delete_from_cleanup:
|
||||
sqlite3AuthContextPop(&sContext);
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
||||
sqlite3ExprListDelete(db, pOrderBy);
|
||||
sqlite3ExprDelete(db, pLimit);
|
||||
#endif
|
||||
sqlite3DbFree(db, aToOpen);
|
||||
return;
|
||||
}
|
||||
@@ -722,7 +766,7 @@ void sqlite3GenerateRowDelete(
|
||||
u8 p5 = 0;
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( pParse->nested==0 ){
|
||||
if( pParse->nested==0 || 0==sqlite3_stricmp(pTab->zName, "sqlite_stat1") ){
|
||||
sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE);
|
||||
}
|
||||
if( eMode!=ONEPASS_OFF ){
|
||||
@@ -853,7 +897,6 @@ int sqlite3GenerateIndexKey(
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
||||
pParse->iSelfTab = iDataCur + 1;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
||||
SQLITE_JUMPIFNULL);
|
||||
pParse->iSelfTab = 0;
|
||||
@@ -900,6 +943,5 @@ int sqlite3GenerateIndexKey(
|
||||
void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
|
||||
if( iLabel ){
|
||||
sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);
|
||||
sqlite3ExprCachePop(pParse);
|
||||
}
|
||||
}
|
||||
|
||||
+436
-363
File diff suppressed because it is too large
Load Diff
+11
-3
@@ -331,6 +331,12 @@ static void fkLookupParent(
|
||||
int iCur = pParse->nTab - 1; /* Cursor number to use */
|
||||
int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */
|
||||
|
||||
sqlite3VdbeVerifyAbortable(v,
|
||||
(!pFKey->isDeferred
|
||||
&& !(pParse->db->flags & SQLITE_DeferFKs)
|
||||
&& !pParse->pToplevel
|
||||
&& !pParse->isMultiWrite) ? OE_Abort : OE_Ignore);
|
||||
|
||||
/* If nIncr is less than zero, then check at runtime if there are any
|
||||
** outstanding constraints to resolve. If there are not, there is no need
|
||||
** to check if deleting this row resolves any outstanding violations.
|
||||
@@ -704,11 +710,12 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
|
||||
*/
|
||||
void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){
|
||||
if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){
|
||||
int iSkip = 0;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
|
||||
assert( v ); /* VDBE has already been allocated */
|
||||
assert( pTab->pSelect==0 ); /* Not a view */
|
||||
if( sqlite3FkReferences(pTab)==0 ){
|
||||
/* Search for a deferred foreign key constraint for which this table
|
||||
** is the child table. If one cannot be found, return without
|
||||
@@ -725,7 +732,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
}
|
||||
|
||||
pParse->disableTriggers = 1;
|
||||
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0);
|
||||
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0);
|
||||
pParse->disableTriggers = 0;
|
||||
|
||||
/* If the DELETE has generated immediate foreign key constraint
|
||||
@@ -738,6 +745,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
** constraints are violated.
|
||||
*/
|
||||
if( (db->flags & SQLITE_DeferFKs)==0 ){
|
||||
sqlite3VdbeVerifyAbortable(v, OE_Abort);
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
@@ -1283,7 +1291,7 @@ static Trigger *fkActionTrigger(
|
||||
sqlite3ExprListAppend(pParse, 0, pRaise),
|
||||
sqlite3SrcListAppend(db, 0, &tFrom, 0),
|
||||
pWhere,
|
||||
0, 0, 0, 0, 0, 0
|
||||
0, 0, 0, 0, 0
|
||||
);
|
||||
pWhere = 0;
|
||||
}
|
||||
|
||||
+206
-62
@@ -35,6 +35,8 @@ static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
|
||||
** iteration of the aggregate loop.
|
||||
*/
|
||||
static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){
|
||||
assert( context->isError<=0 );
|
||||
context->isError = -1;
|
||||
context->skipFlag = 1;
|
||||
}
|
||||
|
||||
@@ -101,8 +103,6 @@ static void lengthFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int len;
|
||||
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
@@ -114,13 +114,17 @@ static void lengthFunc(
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *z = sqlite3_value_text(argv[0]);
|
||||
const unsigned char *z0;
|
||||
unsigned char c;
|
||||
if( z==0 ) return;
|
||||
len = 0;
|
||||
while( *z ){
|
||||
len++;
|
||||
SQLITE_SKIP_UTF8(z);
|
||||
z0 = z;
|
||||
while( (c = *z)!=0 ){
|
||||
z++;
|
||||
if( c>=0xc0 ){
|
||||
while( (*z & 0xc0)==0x80 ){ z++; z0++; }
|
||||
}
|
||||
}
|
||||
sqlite3_result_int(context, len);
|
||||
sqlite3_result_int(context, (int)(z-z0));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -247,7 +251,7 @@ static void printfFunc(
|
||||
x.apArg = argv+1;
|
||||
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
str.printfFlags = SQLITE_PRINTF_SQLFUNC;
|
||||
sqlite3XPrintf(&str, zFormat, &x);
|
||||
sqlite3_str_appendf(&str, zFormat, &x);
|
||||
n = str.nChar;
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
|
||||
SQLITE_DYNAMIC);
|
||||
@@ -698,16 +702,20 @@ static int patternCompare(
|
||||
** c or cx.
|
||||
*/
|
||||
if( c<=0x80 ){
|
||||
u32 cx;
|
||||
char zStop[3];
|
||||
int bMatch;
|
||||
if( noCase ){
|
||||
cx = sqlite3Toupper(c);
|
||||
c = sqlite3Tolower(c);
|
||||
zStop[0] = sqlite3Toupper(c);
|
||||
zStop[1] = sqlite3Tolower(c);
|
||||
zStop[2] = 0;
|
||||
}else{
|
||||
cx = c;
|
||||
zStop[0] = c;
|
||||
zStop[1] = 0;
|
||||
}
|
||||
while( (c2 = *(zString++))!=0 ){
|
||||
if( c2!=c && c2!=cx ) continue;
|
||||
while(1){
|
||||
zString += strcspn((const char*)zString, zStop);
|
||||
if( zString[0]==0 ) break;
|
||||
zString++;
|
||||
bMatch = patternCompare(zPattern,zString,pInfo,matchOther);
|
||||
if( bMatch!=SQLITE_NOMATCH ) return bMatch;
|
||||
}
|
||||
@@ -865,7 +873,8 @@ static void likeFunc(
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_like_count++;
|
||||
#endif
|
||||
sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH);
|
||||
sqlite3_result_int(context,
|
||||
patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1190,6 +1199,8 @@ static void replaceFunc(
|
||||
i64 nOut; /* Maximum size of zOut */
|
||||
int loopLimit; /* Last zStr[] that might match zPattern[] */
|
||||
int i, j; /* Loop counters */
|
||||
unsigned cntExpand; /* Number zOut expansions */
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
assert( argc==3 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
@@ -1221,33 +1232,40 @@ static void replaceFunc(
|
||||
return;
|
||||
}
|
||||
loopLimit = nStr - nPattern;
|
||||
cntExpand = 0;
|
||||
for(i=j=0; i<=loopLimit; i++){
|
||||
if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){
|
||||
zOut[j++] = zStr[i];
|
||||
}else{
|
||||
u8 *zOld;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
nOut += nRep - nPattern;
|
||||
testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
sqlite3_free(zOut);
|
||||
return;
|
||||
}
|
||||
zOld = zOut;
|
||||
zOut = sqlite3_realloc64(zOut, (int)nOut);
|
||||
if( zOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(zOld);
|
||||
return;
|
||||
if( nRep>nPattern ){
|
||||
nOut += nRep - nPattern;
|
||||
testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
sqlite3_free(zOut);
|
||||
return;
|
||||
}
|
||||
cntExpand++;
|
||||
if( (cntExpand&(cntExpand-1))==0 ){
|
||||
/* Grow the size of the output buffer only on substitutions
|
||||
** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */
|
||||
u8 *zOld;
|
||||
zOld = zOut;
|
||||
zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1));
|
||||
if( zOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(zOld);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(&zOut[j], zRep, nRep);
|
||||
j += nRep;
|
||||
i += nPattern-1;
|
||||
}
|
||||
}
|
||||
assert( j+nStr-i+1==nOut );
|
||||
assert( j+nStr-i+1<=nOut );
|
||||
memcpy(&zOut[j], &zStr[i], nStr-i);
|
||||
j += nStr - i;
|
||||
assert( j<=nOut );
|
||||
@@ -1487,7 +1505,7 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
i64 v = sqlite3_value_int64(argv[0]);
|
||||
p->rSum += v;
|
||||
if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){
|
||||
p->overflow = 1;
|
||||
p->approx = p->overflow = 1;
|
||||
}
|
||||
}else{
|
||||
p->rSum += sqlite3_value_double(argv[0]);
|
||||
@@ -1495,6 +1513,32 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
SumCtx *p;
|
||||
int type;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
type = sqlite3_value_numeric_type(argv[0]);
|
||||
/* p is always non-NULL because sumStep() will have been called first
|
||||
** to initialize it */
|
||||
if( ALWAYS(p) && type!=SQLITE_NULL ){
|
||||
assert( p->cnt>0 );
|
||||
p->cnt--;
|
||||
assert( type==SQLITE_INTEGER || p->approx );
|
||||
if( type==SQLITE_INTEGER && p->approx==0 ){
|
||||
i64 v = sqlite3_value_int64(argv[0]);
|
||||
p->rSum -= v;
|
||||
p->iSum -= v;
|
||||
}else{
|
||||
p->rSum -= sqlite3_value_double(argv[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define sumInverse 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
static void sumFinalize(sqlite3_context *context){
|
||||
SumCtx *p;
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
@@ -1529,6 +1573,9 @@ static void totalFinalize(sqlite3_context *context){
|
||||
typedef struct CountCtx CountCtx;
|
||||
struct CountCtx {
|
||||
i64 n;
|
||||
#ifdef SQLITE_DEBUG
|
||||
int bInverse; /* True if xInverse() ever called */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1546,7 +1593,7 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
** sure it still operates correctly, verify that its count agrees with our
|
||||
** internal count when using count(*) and when the total count can be
|
||||
** expressed as a 32-bit integer. */
|
||||
assert( argc==1 || p==0 || p->n>0x7fffffff
|
||||
assert( argc==1 || p==0 || p->n>0x7fffffff || p->bInverse
|
||||
|| p->n==sqlite3_aggregate_count(context) );
|
||||
#endif
|
||||
}
|
||||
@@ -1555,6 +1602,21 @@ static void countFinalize(sqlite3_context *context){
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
sqlite3_result_int64(context, p ? p->n : 0);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void countInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv){
|
||||
CountCtx *p;
|
||||
p = sqlite3_aggregate_context(ctx, sizeof(*p));
|
||||
/* p is always non-NULL since countStep() will have been called first */
|
||||
if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && ALWAYS(p) ){
|
||||
p->n--;
|
||||
#ifdef SQLITE_DEBUG
|
||||
p->bInverse = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define countInverse 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** Routines to implement min() and max() aggregate functions.
|
||||
@@ -1571,7 +1633,7 @@ static void minmaxStep(
|
||||
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
|
||||
if( !pBest ) return;
|
||||
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||||
if( sqlite3_value_type(pArg)==SQLITE_NULL ){
|
||||
if( pBest->flags ) sqlite3SkipAccumulatorLoad(context);
|
||||
}else if( pBest->flags ){
|
||||
int max;
|
||||
@@ -1597,16 +1659,26 @@ static void minmaxStep(
|
||||
sqlite3VdbeMemCopy(pBest, pArg);
|
||||
}
|
||||
}
|
||||
static void minMaxFinalize(sqlite3_context *context){
|
||||
static void minMaxValueFinalize(sqlite3_context *context, int bValue){
|
||||
sqlite3_value *pRes;
|
||||
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
|
||||
if( pRes ){
|
||||
if( pRes->flags ){
|
||||
sqlite3_result_value(context, pRes);
|
||||
}
|
||||
sqlite3VdbeMemRelease(pRes);
|
||||
if( bValue==0 ) sqlite3VdbeMemRelease(pRes);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void minMaxValue(sqlite3_context *context){
|
||||
minMaxValueFinalize(context, 1);
|
||||
}
|
||||
#else
|
||||
# define minMaxValue 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
static void minMaxFinalize(sqlite3_context *context){
|
||||
minMaxValueFinalize(context, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** group_concat(EXPR, ?SEPARATOR?)
|
||||
@@ -1636,20 +1708,52 @@ static void groupConcatStep(
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
}
|
||||
if( zSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
||||
if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
|
||||
}
|
||||
zVal = (char*)sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void groupConcatInverse(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int n;
|
||||
StrAccum *pAccum;
|
||||
assert( argc==1 || argc==2 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
|
||||
/* pAccum is always non-NULL since groupConcatStep() will have always
|
||||
** run frist to initialize it */
|
||||
if( ALWAYS(pAccum) ){
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
if( argc==2 ){
|
||||
n += sqlite3_value_bytes(argv[1]);
|
||||
}else{
|
||||
n++;
|
||||
}
|
||||
if( n>=(int)pAccum->nChar ){
|
||||
pAccum->nChar = 0;
|
||||
}else{
|
||||
pAccum->nChar -= n;
|
||||
memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar);
|
||||
}
|
||||
if( pAccum->nChar==0 ) pAccum->mxAlloc = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define groupConcatInverse 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
StrAccum *pAccum;
|
||||
pAccum = sqlite3_aggregate_context(context, 0);
|
||||
if( pAccum ){
|
||||
if( pAccum->accError==STRACCUM_TOOBIG ){
|
||||
if( pAccum->accError==SQLITE_TOOBIG ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==STRACCUM_NOMEM ){
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
|
||||
@@ -1657,6 +1761,24 @@ static void groupConcatFinalize(sqlite3_context *context){
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void groupConcatValue(sqlite3_context *context){
|
||||
sqlite3_str *pAccum;
|
||||
pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0);
|
||||
if( pAccum ){
|
||||
if( pAccum->accError==SQLITE_TOOBIG ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
const char *zText = sqlite3_str_value(pAccum);
|
||||
sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define groupConcatValue 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** This routine does per-connection function registration. Most
|
||||
@@ -1694,10 +1816,10 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
}else{
|
||||
pInfo = (struct compareInfo*)&likeInfoNorm;
|
||||
}
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0, 0, 0);
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
||||
setLikeOptFlag(db, "like",
|
||||
caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
|
||||
@@ -1706,9 +1828,14 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
/*
|
||||
** pExpr points to an expression which implements a function. If
|
||||
** it is appropriate to apply the LIKE optimization to that function
|
||||
** then set aWc[0] through aWc[2] to the wildcard characters and
|
||||
** return TRUE. If the function is not a LIKE-style function then
|
||||
** return FALSE.
|
||||
** then set aWc[0] through aWc[2] to the wildcard characters and the
|
||||
** escape character and then return TRUE. If the function is not a
|
||||
** LIKE-style function then return FALSE.
|
||||
**
|
||||
** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
|
||||
** operator if c is a string literal that is exactly one byte in length.
|
||||
** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is
|
||||
** no ESCAPE clause.
|
||||
**
|
||||
** *pIsNocase is set to true if uppercase and lowercase are equivalent for
|
||||
** the function (default for LIKE). If the function makes the distinction
|
||||
@@ -1717,17 +1844,26 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
*/
|
||||
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
FuncDef *pDef;
|
||||
if( pExpr->op!=TK_FUNCTION
|
||||
|| !pExpr->x.pList
|
||||
|| pExpr->x.pList->nExpr!=2
|
||||
){
|
||||
int nExpr;
|
||||
if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
|
||||
nExpr = pExpr->x.pList->nExpr;
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
|
||||
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( nExpr<3 ){
|
||||
aWc[3] = 0;
|
||||
}else{
|
||||
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
|
||||
char *zEscape;
|
||||
if( pEscape->op!=TK_STRING ) return 0;
|
||||
zEscape = pEscape->u.zToken;
|
||||
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
|
||||
aWc[3] = zEscape[0];
|
||||
}
|
||||
|
||||
/* The memcpy() statement assumes that the wildcard characters are
|
||||
** the first three statements in the compareInfo structure. The
|
||||
@@ -1779,6 +1915,10 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
#ifdef SQLITE_DEBUG
|
||||
FUNCTION2(affinity, 1, 0, 0, noopFunc, SQLITE_FUNC_AFFINITY),
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET|
|
||||
SQLITE_FUNC_TYPEOF),
|
||||
#endif
|
||||
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
|
||||
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
|
||||
@@ -1788,11 +1928,11 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION(trim, 2, 3, 0, trimFunc ),
|
||||
FUNCTION(min, -1, 0, 1, minmaxFunc ),
|
||||
FUNCTION(min, 0, 0, 1, 0 ),
|
||||
AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
|
||||
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
|
||||
SQLITE_FUNC_MINMAX ),
|
||||
FUNCTION(max, -1, 1, 1, minmaxFunc ),
|
||||
FUNCTION(max, 0, 1, 1, 0 ),
|
||||
AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
|
||||
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
|
||||
SQLITE_FUNC_MINMAX ),
|
||||
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
||||
@@ -1823,14 +1963,17 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
|
||||
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
||||
AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
|
||||
SQLITE_FUNC_COUNT ),
|
||||
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
|
||||
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
|
||||
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
|
||||
WAGGREGATE(count, 0,0,0, countStep,
|
||||
countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
|
||||
WAGGREGATE(count, 1,0,0, countStep,
|
||||
countFinalize, countFinalize, countInverse, 0 ),
|
||||
WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
|
||||
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
|
||||
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
|
||||
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
|
||||
|
||||
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
@@ -1850,6 +1993,7 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
sqlite3WindowFunctions();
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
|
||||
+10
-4
@@ -199,6 +199,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_THREADSAFE==1, /* bFullMutex */
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
|
||||
0, /* bSmallMalloc */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
0, /* neverCorrupt */
|
||||
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
|
||||
@@ -211,9 +212,6 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, 0, /* mnHeap, mxHeap */
|
||||
SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */
|
||||
SQLITE_MAX_MMAP_SIZE, /* mxMmap */
|
||||
(void*)0, /* pScratch */
|
||||
0, /* szScratch */
|
||||
0, /* nScratch */
|
||||
(void*)0, /* pPage */
|
||||
0, /* szPage */
|
||||
SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
|
||||
@@ -242,7 +240,8 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* xTestCallback */
|
||||
#endif
|
||||
0, /* bLocaltimeFault */
|
||||
0x7ffffffe /* iOnceResetThreshold */
|
||||
0x7ffffffe, /* iOnceResetThreshold */
|
||||
SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -260,6 +259,13 @@ const Token sqlite3IntTokens[] = {
|
||||
{ "1", 1 }
|
||||
};
|
||||
|
||||
#ifdef VDBE_PROFILE
|
||||
/*
|
||||
** The following performance counter can be used in place of
|
||||
** sqlite3Hwtime() for profiling. This is a no-op on standard builds.
|
||||
*/
|
||||
sqlite3_uint64 sqlite3NProfileCnt = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The value of the "pending" byte must be 0x40000000 (1 byte past the
|
||||
|
||||
+211
-68
@@ -210,11 +210,12 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
** first use of table pTab. On 2nd and subsequent uses, the original
|
||||
** AutoincInfo structure is used.
|
||||
**
|
||||
** Three memory locations are allocated:
|
||||
** Four consecutive registers are allocated:
|
||||
**
|
||||
** (1) Register to hold the name of the pTab table.
|
||||
** (2) Register to hold the maximum ROWID of pTab.
|
||||
** (3) Register to hold the rowid in sqlite_sequence of pTab
|
||||
** (1) The name of the pTab table.
|
||||
** (2) The maximum ROWID of pTab.
|
||||
** (3) The rowid in sqlite_sequence of pTab
|
||||
** (4) The original value of the max ROWID in pTab, or NULL if none
|
||||
**
|
||||
** The 2nd register is the one that is returned. That is all the
|
||||
** insert routine needs to know about.
|
||||
@@ -225,11 +226,26 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
assert( pParse->db->aDb[iDb].pSchema!=0 );
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->flags & SQLITE_Vacuum)==0
|
||||
&& (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
Table *pSeqTab = pParse->db->aDb[iDb].pSchema->pSeqTab;
|
||||
|
||||
/* Verify that the sqlite_sequence table exists and is an ordinary
|
||||
** rowid table with exactly two columns.
|
||||
** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */
|
||||
if( pSeqTab==0
|
||||
|| !HasRowid(pSeqTab)
|
||||
|| IsVirtual(pSeqTab)
|
||||
|| pSeqTab->nCol!=2
|
||||
){
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_CORRUPT_SEQUENCE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pInfo = pToplevel->pAinc;
|
||||
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
|
||||
@@ -242,7 +258,7 @@ static int autoIncBegin(
|
||||
pInfo->iDb = iDb;
|
||||
pToplevel->nMem++; /* Register to hold name of table */
|
||||
pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */
|
||||
pToplevel->nMem++; /* Rowid in sqlite_sequence */
|
||||
pToplevel->nMem +=2; /* Rowid in sqlite_sequence + orig max val */
|
||||
}
|
||||
memId = pInfo->regCtr;
|
||||
}
|
||||
@@ -270,15 +286,17 @@ void sqlite3AutoincrementBegin(Parse *pParse){
|
||||
static const int iLn = VDBE_OFFSET_LINENO(2);
|
||||
static const VdbeOpList autoInc[] = {
|
||||
/* 0 */ {OP_Null, 0, 0, 0},
|
||||
/* 1 */ {OP_Rewind, 0, 9, 0},
|
||||
/* 1 */ {OP_Rewind, 0, 10, 0},
|
||||
/* 2 */ {OP_Column, 0, 0, 0},
|
||||
/* 3 */ {OP_Ne, 0, 7, 0},
|
||||
/* 3 */ {OP_Ne, 0, 9, 0},
|
||||
/* 4 */ {OP_Rowid, 0, 0, 0},
|
||||
/* 5 */ {OP_Column, 0, 1, 0},
|
||||
/* 6 */ {OP_Goto, 0, 9, 0},
|
||||
/* 7 */ {OP_Next, 0, 2, 0},
|
||||
/* 8 */ {OP_Integer, 0, 0, 0},
|
||||
/* 9 */ {OP_Close, 0, 0, 0}
|
||||
/* 6 */ {OP_AddImm, 0, 0, 0},
|
||||
/* 7 */ {OP_Copy, 0, 0, 0},
|
||||
/* 8 */ {OP_Goto, 0, 11, 0},
|
||||
/* 9 */ {OP_Next, 0, 2, 0},
|
||||
/* 10 */ {OP_Integer, 0, 0, 0},
|
||||
/* 11 */ {OP_Close, 0, 0, 0}
|
||||
};
|
||||
VdbeOp *aOp;
|
||||
pDb = &db->aDb[p->iDb];
|
||||
@@ -289,14 +307,17 @@ void sqlite3AutoincrementBegin(Parse *pParse){
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
|
||||
if( aOp==0 ) break;
|
||||
aOp[0].p2 = memId;
|
||||
aOp[0].p3 = memId+1;
|
||||
aOp[0].p3 = memId+2;
|
||||
aOp[2].p3 = memId;
|
||||
aOp[3].p1 = memId-1;
|
||||
aOp[3].p3 = memId;
|
||||
aOp[3].p5 = SQLITE_JUMPIFNULL;
|
||||
aOp[4].p2 = memId+1;
|
||||
aOp[5].p3 = memId;
|
||||
aOp[8].p2 = memId;
|
||||
aOp[6].p1 = memId;
|
||||
aOp[7].p2 = memId+2;
|
||||
aOp[7].p1 = memId;
|
||||
aOp[10].p2 = memId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +364,8 @@ static SQLITE_NOINLINE void autoIncrementEnd(Parse *pParse){
|
||||
|
||||
iRec = sqlite3GetTempReg(pParse);
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
|
||||
sqlite3VdbeAddOp3(v, OP_Le, memId+2, sqlite3VdbeCurrentAddr(v)+7, memId);
|
||||
VdbeCoverage(v);
|
||||
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
|
||||
if( aOp==0 ) break;
|
||||
@@ -480,11 +503,11 @@ void sqlite3Insert(
|
||||
SrcList *pTabList, /* Name of table into which we are inserting */
|
||||
Select *pSelect, /* A SELECT statement to use as the data source */
|
||||
IdList *pColumn, /* Column names corresponding to IDLIST. */
|
||||
int onError /* How to handle constraint errors */
|
||||
int onError, /* How to handle constraint errors */
|
||||
Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
|
||||
){
|
||||
sqlite3 *db; /* The main database structure */
|
||||
Table *pTab; /* The table to insert into. aka TABLE */
|
||||
char *zTab; /* Name of the table into which we are inserting */
|
||||
int i, j; /* Loop counters */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
@@ -540,8 +563,6 @@ void sqlite3Insert(
|
||||
/* Locate the table into which we will be inserting new information.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
zTab = pTabList->a[0].zName;
|
||||
if( NEVER(zTab==0) ) goto insert_cleanup;
|
||||
pTab = sqlite3SrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ){
|
||||
goto insert_cleanup;
|
||||
@@ -778,7 +799,10 @@ void sqlite3Insert(
|
||||
|
||||
/* Initialize the count of rows to be inserted
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
if( (db->flags & SQLITE_CountRows)!=0
|
||||
&& !pParse->nested
|
||||
&& !pParse->pTriggerTab
|
||||
){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
@@ -798,6 +822,19 @@ void sqlite3Insert(
|
||||
pParse->nMem += pIdx->nColumn;
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pUpsert ){
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
pUpsert->pUpsertSrc = pTabList;
|
||||
pUpsert->regData = regData;
|
||||
pUpsert->iDataCur = iDataCur;
|
||||
pUpsert->iIdxCur = iIdxCur;
|
||||
if( pUpsert->pUpsertTarget ){
|
||||
sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* This is the top of the main insertion loop */
|
||||
if( useTempTable ){
|
||||
@@ -912,7 +949,8 @@ void sqlite3Insert(
|
||||
VdbeOp *pOp;
|
||||
sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
|
||||
pOp = sqlite3VdbeGetOp(v, -1);
|
||||
if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
||||
assert( pOp!=0 );
|
||||
if( pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
||||
appendFlag = 1;
|
||||
pOp->opcode = OP_NewRowid;
|
||||
pOp->p1 = iDataCur;
|
||||
@@ -999,7 +1037,7 @@ void sqlite3Insert(
|
||||
int isReplace; /* Set to true if constraints may cause a replace */
|
||||
int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
||||
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
|
||||
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert
|
||||
);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
|
||||
|
||||
@@ -1022,7 +1060,7 @@ void sqlite3Insert(
|
||||
|
||||
/* Update the count of rows that are inserted
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
||||
}
|
||||
|
||||
@@ -1059,7 +1097,7 @@ insert_end:
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
||||
@@ -1068,6 +1106,7 @@ insert_end:
|
||||
insert_cleanup:
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3UpsertDelete(db, pUpsert);
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
sqlite3IdListDelete(db, pColumn);
|
||||
sqlite3DbFree(db, aRegIdx);
|
||||
@@ -1234,7 +1273,8 @@ void sqlite3GenerateConstraintChecks(
|
||||
u8 overrideError, /* Override onError to this if not OE_Default */
|
||||
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
|
||||
int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
|
||||
int *aiChng /* column i is unchanged if aiChng[i]<0 */
|
||||
int *aiChng, /* column i is unchanged if aiChng[i]<0 */
|
||||
Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */
|
||||
){
|
||||
Vdbe *v; /* VDBE under constrution */
|
||||
Index *pIdx; /* Pointer to one of the indices */
|
||||
@@ -1247,10 +1287,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
int addr1; /* Address of jump instruction */
|
||||
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
|
||||
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
|
||||
int ipkTop = 0; /* Top of the rowid change constraint check */
|
||||
int ipkBottom = 0; /* Bottom of the rowid change constraint check */
|
||||
Index *pUpIdx = 0; /* Index to which to apply the upsert */
|
||||
u8 isUpdate; /* True if this is an UPDATE operation */
|
||||
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
|
||||
int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
|
||||
int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */
|
||||
int ipkTop = 0; /* Top of the IPK uniqueness check */
|
||||
int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
|
||||
|
||||
isUpdate = regOldData!=0;
|
||||
db = pParse->db;
|
||||
@@ -1340,6 +1383,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
Expr *pExpr = pCheck->a[i].pExpr;
|
||||
if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
|
||||
allOk = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
|
||||
if( onError==OE_Ignore ){
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
@@ -1357,6 +1401,50 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
|
||||
/* UNIQUE and PRIMARY KEY constraints should be handled in the following
|
||||
** order:
|
||||
**
|
||||
** (1) OE_Update
|
||||
** (2) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
|
||||
** (3) OE_Replace
|
||||
**
|
||||
** OE_Fail and OE_Ignore must happen before any changes are made.
|
||||
** OE_Update guarantees that only a single row will change, so it
|
||||
** must happen before OE_Replace. Technically, OE_Abort and OE_Rollback
|
||||
** could happen in any order, but they are grouped up front for
|
||||
** convenience.
|
||||
**
|
||||
** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43
|
||||
** The order of constraints used to have OE_Update as (2) and OE_Abort
|
||||
** and so forth as (1). But apparently PostgreSQL checks the OE_Update
|
||||
** constraint before any others, so it had to be moved.
|
||||
**
|
||||
** Constraint checking code is generated in this order:
|
||||
** (A) The rowid constraint
|
||||
** (B) Unique index constraints that do not have OE_Replace as their
|
||||
** default conflict resolution strategy
|
||||
** (C) Unique index that do use OE_Replace by default.
|
||||
**
|
||||
** The ordering of (2) and (3) is accomplished by making sure the linked
|
||||
** list of indexes attached to a table puts all OE_Replace indexes last
|
||||
** in the list. See sqlite3CreateIndex() for where that happens.
|
||||
*/
|
||||
|
||||
if( pUpsert ){
|
||||
if( pUpsert->pUpsertTarget==0 ){
|
||||
/* An ON CONFLICT DO NOTHING clause, without a constraint-target.
|
||||
** Make all unique constraint resolution be OE_Ignore */
|
||||
assert( pUpsert->pUpsertSet==0 );
|
||||
overrideError = OE_Ignore;
|
||||
pUpsert = 0;
|
||||
}else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
|
||||
/* If the constraint-target uniqueness check must be run first.
|
||||
** Jump to that uniqueness check now */
|
||||
upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
VdbeComment((v, "UPSERT constraint goes first"));
|
||||
}
|
||||
}
|
||||
|
||||
/* If rowid is changing, make sure the new rowid does not previously
|
||||
** exist in the table.
|
||||
*/
|
||||
@@ -1371,6 +1459,28 @@ void sqlite3GenerateConstraintChecks(
|
||||
onError = OE_Abort;
|
||||
}
|
||||
|
||||
/* figure out whether or not upsert applies in this case */
|
||||
if( pUpsert && pUpsert->pUpsertIdx==0 ){
|
||||
if( pUpsert->pUpsertSet==0 ){
|
||||
onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
|
||||
}else{
|
||||
onError = OE_Update; /* DO UPDATE */
|
||||
}
|
||||
}
|
||||
|
||||
/* If the response to a rowid conflict is REPLACE but the response
|
||||
** to some other UNIQUE constraint is FAIL or IGNORE, then we need
|
||||
** to defer the running of the rowid conflict checking until after
|
||||
** the UNIQUE constraints have run.
|
||||
*/
|
||||
if( onError==OE_Replace /* IPK rule is REPLACE */
|
||||
&& onError!=overrideError /* Rules for other contraints are different */
|
||||
&& pTab->pIndex /* There exist other constraints */
|
||||
){
|
||||
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
|
||||
VdbeComment((v, "defer IPK REPLACE until last"));
|
||||
}
|
||||
|
||||
if( isUpdate ){
|
||||
/* pkChng!=0 does not mean that the rowid has changed, only that
|
||||
** it might have changed. Skip the conflict logic below if the rowid
|
||||
@@ -1380,26 +1490,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* If the response to a rowid conflict is REPLACE but the response
|
||||
** to some other UNIQUE constraint is FAIL or IGNORE, then we need
|
||||
** to defer the running of the rowid conflict checking until after
|
||||
** the UNIQUE constraints have run.
|
||||
*/
|
||||
if( onError==OE_Replace && overrideError!=OE_Replace ){
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){
|
||||
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if the new rowid already exists in the table. Skip
|
||||
** the following conflict logic if it does not. */
|
||||
VdbeNoopComment((v, "uniqueness check for ROWID"));
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
|
||||
VdbeCoverage(v);
|
||||
|
||||
/* Generate code that deals with a rowid collision */
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
@@ -1408,6 +1505,9 @@ void sqlite3GenerateConstraintChecks(
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
testcase( onError==OE_Rollback );
|
||||
testcase( onError==OE_Abort );
|
||||
testcase( onError==OE_Fail );
|
||||
sqlite3RowidConstraint(pParse, onError, pTab);
|
||||
break;
|
||||
}
|
||||
@@ -1444,14 +1544,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
regNewData, 1, 0, OE_Replace, 1, -1);
|
||||
}else{
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( HasRowid(pTab) ){
|
||||
/* This OP_Delete opcode fires the pre-update-hook only. It does
|
||||
** not modify the b-tree. It is more efficient to let the coming
|
||||
** OP_Insert replace the existing entry than it is to delete the
|
||||
** existing entry and then insert a new one. */
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
}
|
||||
assert( HasRowid(pTab) );
|
||||
/* This OP_Delete opcode fires the pre-update-hook only. It does
|
||||
** not modify the b-tree. It is more efficient to let the coming
|
||||
** OP_Insert replace the existing entry than it is to delete the
|
||||
** existing entry and then insert a new one. */
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
if( pTab->pIndex ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
@@ -1461,8 +1560,14 @@ void sqlite3GenerateConstraintChecks(
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
|
||||
/* Fall through */
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
/*assert( seenReplace==0 );*/
|
||||
testcase( onError==OE_Ignore );
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
break;
|
||||
}
|
||||
@@ -1470,7 +1575,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeResolveLabel(v, addrRowidOk);
|
||||
if( ipkTop ){
|
||||
ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, ipkTop);
|
||||
sqlite3VdbeJumpHere(v, ipkTop-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1488,12 +1593,21 @@ void sqlite3GenerateConstraintChecks(
|
||||
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
|
||||
|
||||
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
|
||||
if( bAffinityDone==0 ){
|
||||
if( pUpIdx==pIdx ){
|
||||
addrUniqueOk = upsertJump+1;
|
||||
upsertBypass = sqlite3VdbeGoto(v, 0);
|
||||
VdbeComment((v, "Skip upsert subroutine"));
|
||||
sqlite3VdbeJumpHere(v, upsertJump);
|
||||
}else{
|
||||
addrUniqueOk = sqlite3VdbeMakeLabel(v);
|
||||
}
|
||||
if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
|
||||
sqlite3TableAffinity(v, pTab, regNewData+1);
|
||||
bAffinityDone = 1;
|
||||
}
|
||||
VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
|
||||
iThisCur = iIdxCur+ix;
|
||||
addrUniqueOk = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
|
||||
/* Skip partial indices for which the WHERE clause is not true */
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
@@ -1553,6 +1667,15 @@ void sqlite3GenerateConstraintChecks(
|
||||
onError = OE_Abort;
|
||||
}
|
||||
|
||||
/* Figure out if the upsert clause applies to this index */
|
||||
if( pUpIdx==pIdx ){
|
||||
if( pUpsert->pUpsertSet==0 ){
|
||||
onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
|
||||
}else{
|
||||
onError = OE_Update; /* DO UPDATE */
|
||||
}
|
||||
}
|
||||
|
||||
/* Collision detection may be omitted if all of the following are true:
|
||||
** (1) The conflict resolution algorithm is REPLACE
|
||||
** (2) The table is a WITHOUT ROWID table
|
||||
@@ -1573,6 +1696,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
|
||||
regIdx, pIdx->nKeyCol); VdbeCoverage(v);
|
||||
|
||||
@@ -1634,25 +1758,37 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
/* Generate code that executes if the new index entry is not unique */
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
|| onError==OE_Ignore || onError==OE_Replace || onError==OE_Update );
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
testcase( onError==OE_Rollback );
|
||||
testcase( onError==OE_Abort );
|
||||
testcase( onError==OE_Fail );
|
||||
sqlite3UniqueConstraint(pParse, onError, pIdx);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
|
||||
/* Fall through */
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
testcase( onError==OE_Ignore );
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Trigger *pTrigger = 0;
|
||||
assert( onError==OE_Replace );
|
||||
sqlite3MultiWrite(pParse);
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
}
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regR, nPkField, 0, OE_Replace,
|
||||
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
|
||||
@@ -1660,14 +1796,22 @@ void sqlite3GenerateConstraintChecks(
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
||||
if( pUpIdx==pIdx ){
|
||||
sqlite3VdbeGoto(v, upsertJump+1);
|
||||
sqlite3VdbeJumpHere(v, upsertBypass);
|
||||
}else{
|
||||
sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
||||
}
|
||||
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
|
||||
}
|
||||
|
||||
/* If the IPK constraint is a REPLACE, run it last */
|
||||
if( ipkTop ){
|
||||
sqlite3VdbeGoto(v, ipkTop+1);
|
||||
VdbeComment((v, "Do IPK REPLACE"));
|
||||
sqlite3VdbeJumpHere(v, ipkBottom);
|
||||
}
|
||||
|
||||
|
||||
*pbMayReplace = seenReplace;
|
||||
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
|
||||
}
|
||||
@@ -1763,7 +1907,6 @@ void sqlite3CompleteInsertion(
|
||||
sqlite3SetMakeRecordP5(v, pTab);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
|
||||
}
|
||||
if( pParse->nested ){
|
||||
pik_flags = 0;
|
||||
@@ -2009,7 +2152,6 @@ static int xferOptimization(
|
||||
if( pSelect->pLimit ){
|
||||
return 0; /* SELECT may not have a LIMIT clause */
|
||||
}
|
||||
assert( pSelect->pOffset==0 ); /* Must be so if pLimit==0 */
|
||||
if( pSelect->pPrior ){
|
||||
return 0; /* SELECT may not be a compound query */
|
||||
}
|
||||
@@ -2059,7 +2201,7 @@ static int xferOptimization(
|
||||
Column *pDestCol = &pDest->aCol[i];
|
||||
Column *pSrcCol = &pSrc->aCol[i];
|
||||
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
|
||||
if( (db->flags & SQLITE_Vacuum)==0
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
|
||||
){
|
||||
return 0; /* Neither table may have __hidden__ columns */
|
||||
@@ -2135,15 +2277,15 @@ static int xferOptimization(
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
|
||||
assert( HasRowid(pDest) || destHasUniqueIdx );
|
||||
if( (db->flags & SQLITE_Vacuum)==0 && (
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
|
||||
(pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|
||||
|| destHasUniqueIdx /* (2) */
|
||||
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
|
||||
)){
|
||||
/* In some circumstances, we are able to run the xfer optimization
|
||||
** only if the destination table is initially empty. Unless the
|
||||
** SQLITE_Vacuum flag is set, this block generates code to make
|
||||
** that determination. If SQLITE_Vacuum is set, then the destination
|
||||
** DBFLAG_Vacuum flag is set, this block generates code to make
|
||||
** that determination. If DBFLAG_Vacuum is set, then the destination
|
||||
** table is always empty.
|
||||
**
|
||||
** Conditions under which the destination must be empty:
|
||||
@@ -2167,6 +2309,7 @@ static int xferOptimization(
|
||||
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
if( pDest->iPKey>=0 ){
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3RowidConstraint(pParse, onError, pDest);
|
||||
@@ -2179,8 +2322,8 @@ static int xferOptimization(
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
|
||||
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
}else{
|
||||
@@ -2211,13 +2354,13 @@ static int xferOptimization(
|
||||
VdbeComment((v, "%s", pDestIdx->zName));
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
/* This INSERT command is part of a VACUUM operation, which guarantees
|
||||
** that the destination table is empty. If all indexed columns use
|
||||
** collation sequence BINARY, then it can also be assumed that the
|
||||
** index will be populated by inserting keys in strictly sorted
|
||||
** order. In this case, instead of seeking within the b-tree as part
|
||||
** of every OP_IdxInsert opcode, an OP_Last is added before the
|
||||
** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the
|
||||
** OP_IdxInsert to seek to the point within the b-tree where each key
|
||||
** should be inserted. This is faster.
|
||||
**
|
||||
@@ -2232,7 +2375,7 @@ static int xferOptimization(
|
||||
}
|
||||
if( i==pSrcIdx->nColumn ){
|
||||
idxInsFlags = OPFLAG_USESEEKRESULT;
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
}
|
||||
}
|
||||
if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
|
||||
|
||||
+22
-1
@@ -430,7 +430,28 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_prepare16_v3,
|
||||
sqlite3_bind_pointer,
|
||||
sqlite3_result_pointer,
|
||||
sqlite3_value_pointer
|
||||
sqlite3_value_pointer,
|
||||
/* Version 3.22.0 and later */
|
||||
sqlite3_vtab_nochange,
|
||||
sqlite3_value_nochange,
|
||||
sqlite3_vtab_collation,
|
||||
/* Version 3.24.0 and later */
|
||||
sqlite3_keyword_count,
|
||||
sqlite3_keyword_name,
|
||||
sqlite3_keyword_check,
|
||||
sqlite3_str_new,
|
||||
sqlite3_str_finish,
|
||||
sqlite3_str_appendf,
|
||||
sqlite3_str_vappendf,
|
||||
sqlite3_str_append,
|
||||
sqlite3_str_appendall,
|
||||
sqlite3_str_appendchar,
|
||||
sqlite3_str_reset,
|
||||
sqlite3_str_errcode,
|
||||
sqlite3_str_length,
|
||||
sqlite3_str_value,
|
||||
/* Version 3.25.0 and later */
|
||||
sqlite3_create_window_function
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+308
-145
@@ -22,7 +22,7 @@
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
# include "rtree.h"
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
# include "sqliteicu.h"
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
@@ -47,9 +47,11 @@ const char sqlite3_version[] = SQLITE_VERSION;
|
||||
*/
|
||||
const char *sqlite3_libversion(void){ return sqlite3_version; }
|
||||
|
||||
/* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a
|
||||
/* IMPLEMENTATION-OF: R-25063-23286 The sqlite3_sourceid() function returns a
|
||||
** pointer to a string constant whose value is the same as the
|
||||
** SQLITE_SOURCE_ID C preprocessor macro.
|
||||
** SQLITE_SOURCE_ID C preprocessor macro. Except if SQLite is built using
|
||||
** an edited copy of the amalgamation, then the last four characters of
|
||||
** the hash might be different from SQLITE_SOURCE_ID.
|
||||
*/
|
||||
const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
||||
|
||||
@@ -237,6 +239,11 @@ int sqlite3_initialize(void){
|
||||
sqlite3GlobalConfig.isPCacheInit = 1;
|
||||
rc = sqlite3OsInit();
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3MemdbInit();
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
|
||||
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
|
||||
@@ -269,7 +276,7 @@ int sqlite3_initialize(void){
|
||||
#ifndef NDEBUG
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/* This section of code's only "output" is via assert() statements. */
|
||||
if ( rc==SQLITE_OK ){
|
||||
if( rc==SQLITE_OK ){
|
||||
u64 x = (((u64)1)<<63)-1;
|
||||
double y;
|
||||
assert(sizeof(x)==8);
|
||||
@@ -436,14 +443,8 @@ int sqlite3_config(int op, ...){
|
||||
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_SCRATCH: {
|
||||
/* EVIDENCE-OF: R-08404-60887 There are three arguments to
|
||||
** SQLITE_CONFIG_SCRATCH: A pointer an 8-byte aligned memory buffer from
|
||||
** which the scratch allocations will be drawn, the size of each scratch
|
||||
** allocation (sz), and the maximum number of scratch allocations (N). */
|
||||
sqlite3GlobalConfig.pScratch = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.szScratch = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nScratch = va_arg(ap, int);
|
||||
case SQLITE_CONFIG_SMALL_MALLOC: {
|
||||
sqlite3GlobalConfig.bSmallMalloc = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_PAGECACHE: {
|
||||
@@ -641,6 +642,17 @@ int sqlite3_config(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
||||
case SQLITE_CONFIG_SORTERREF_SIZE: {
|
||||
int iVal = va_arg(ap, int);
|
||||
if( iVal<0 ){
|
||||
iVal = SQLITE_DEFAULT_SORTERREF_SIZE;
|
||||
}
|
||||
sqlite3GlobalConfig.szSorterRef = (u32)iVal;
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SORTER_REFERENCES */
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@@ -664,7 +676,8 @@ int sqlite3_config(int op, ...){
|
||||
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
void *pStart;
|
||||
if( db->lookaside.nOut ){
|
||||
|
||||
if( sqlite3LookasideUsed(db,0)>0 ){
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
/* Free any existing lookaside buffer for this handle before
|
||||
@@ -692,16 +705,18 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
pStart = pBuf;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
db->lookaside.pInit = 0;
|
||||
db->lookaside.pFree = 0;
|
||||
db->lookaside.sz = (u16)sz;
|
||||
if( pStart ){
|
||||
int i;
|
||||
LookasideSlot *p;
|
||||
assert( sz > (int)sizeof(LookasideSlot*) );
|
||||
db->lookaside.nSlot = cnt;
|
||||
p = (LookasideSlot*)pStart;
|
||||
for(i=cnt-1; i>=0; i--){
|
||||
p->pNext = db->lookaside.pFree;
|
||||
db->lookaside.pFree = p;
|
||||
p->pNext = db->lookaside.pInit;
|
||||
db->lookaside.pInit = p;
|
||||
p = (LookasideSlot*)&((u8*)p)[sz];
|
||||
}
|
||||
db->lookaside.pEnd = p;
|
||||
@@ -712,6 +727,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
db->lookaside.pEnd = db;
|
||||
db->lookaside.bDisable = 1;
|
||||
db->lookaside.bMalloced = 0;
|
||||
db->lookaside.nSlot = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOOKASIDE */
|
||||
return SQLITE_OK;
|
||||
@@ -817,6 +833,8 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
|
||||
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
|
||||
{ SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG },
|
||||
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
|
||||
{ SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
|
||||
};
|
||||
unsigned int i;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
@@ -824,14 +842,14 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
if( aFlagOp[i].op==op ){
|
||||
int onoff = va_arg(ap, int);
|
||||
int *pRes = va_arg(ap, int*);
|
||||
int oldFlags = db->flags;
|
||||
u32 oldFlags = db->flags;
|
||||
if( onoff>0 ){
|
||||
db->flags |= aFlagOp[i].mask;
|
||||
}else if( onoff==0 ){
|
||||
db->flags &= ~aFlagOp[i].mask;
|
||||
}
|
||||
if( oldFlags!=db->flags ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
}
|
||||
if( pRes ){
|
||||
*pRes = (db->flags & aFlagOp[i].mask)!=0;
|
||||
@@ -892,6 +910,15 @@ static int binCollFunc(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if CollSeq is the default built-in BINARY.
|
||||
*/
|
||||
int sqlite3IsBinary(const CollSeq *p){
|
||||
assert( p==0 || p->xCmp!=binCollFunc || p->pUser!=0
|
||||
|| strcmp(p->zName,"BINARY")==0 );
|
||||
return p==0 || (p->xCmp==binCollFunc && p->pUser==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Another built-in collating sequence: NOCASE.
|
||||
**
|
||||
@@ -1013,7 +1040,7 @@ static void disconnectAllVtab(sqlite3 *db){
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Schema *pSchema = db->aDb[i].pSchema;
|
||||
if( db->aDb[i].pSchema ){
|
||||
if( pSchema ){
|
||||
for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
|
||||
Table *pTab = (Table *)sqliteHashData(p);
|
||||
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
|
||||
@@ -1231,7 +1258,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
db->magic = SQLITE_MAGIC_CLOSED;
|
||||
sqlite3_mutex_free(db->mutex);
|
||||
assert( db->lookaside.nOut==0 ); /* Fails on a lookaside memory leak */
|
||||
assert( sqlite3LookasideUsed(db,0)==0 );
|
||||
if( db->lookaside.bMalloced ){
|
||||
sqlite3_free(db->lookaside.pStart);
|
||||
}
|
||||
@@ -1259,7 +1286,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
** the database rollback and schema reset, which can cause false
|
||||
** corruption reports in some cases. */
|
||||
sqlite3BtreeEnterAll(db);
|
||||
schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
|
||||
schemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0 && db->init.busy==0;
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
@@ -1273,8 +1300,8 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
sqlite3VtabRollback(db);
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
if( schemaChange ){
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
@@ -1302,6 +1329,7 @@ const char *sqlite3ErrName(int rc){
|
||||
switch( rc ){
|
||||
case SQLITE_OK: zName = "SQLITE_OK"; break;
|
||||
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
|
||||
case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
|
||||
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
|
||||
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
|
||||
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
||||
@@ -1314,9 +1342,10 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
||||
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
|
||||
case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
|
||||
case SQLITE_READONLY_CANTINIT: zName = "SQLITE_READONLY_CANTINIT"; break;
|
||||
case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
|
||||
case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
|
||||
case SQLITE_READONLY_DIRECTORY: zName = "SQLITE_READONLY_DIRECTORY";break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
|
||||
@@ -1436,6 +1465,8 @@ const char *sqlite3ErrStr(int rc){
|
||||
/* SQLITE_FORMAT */ 0,
|
||||
/* SQLITE_RANGE */ "column index out of range",
|
||||
/* SQLITE_NOTADB */ "file is not a database",
|
||||
/* SQLITE_NOTICE */ "notification message",
|
||||
/* SQLITE_WARNING */ "warning message",
|
||||
};
|
||||
const char *zErr = "unknown error";
|
||||
switch( rc ){
|
||||
@@ -1443,6 +1474,14 @@ const char *sqlite3ErrStr(int rc){
|
||||
zErr = "abort due to ROLLBACK";
|
||||
break;
|
||||
}
|
||||
case SQLITE_ROW: {
|
||||
zErr = "another row available";
|
||||
break;
|
||||
}
|
||||
case SQLITE_DONE: {
|
||||
zErr = "no more rows available";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rc &= 0xff;
|
||||
if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
|
||||
@@ -1459,21 +1498,40 @@ const char *sqlite3ErrStr(int rc){
|
||||
** again until a timeout value is reached. The timeout value is
|
||||
** an integer number of milliseconds passed in as the first
|
||||
** argument.
|
||||
**
|
||||
** Return non-zero to retry the lock. Return zero to stop trying
|
||||
** and cause SQLite to return SQLITE_BUSY.
|
||||
*/
|
||||
static int sqliteDefaultBusyCallback(
|
||||
void *ptr, /* Database connection */
|
||||
int count /* Number of times table has been busy */
|
||||
void *ptr, /* Database connection */
|
||||
int count, /* Number of times table has been busy */
|
||||
sqlite3_file *pFile /* The file on which the lock occurred */
|
||||
){
|
||||
#if SQLITE_OS_WIN || HAVE_USLEEP
|
||||
/* This case is for systems that have support for sleeping for fractions of
|
||||
** a second. Examples: All windows systems, unix systems with usleep() */
|
||||
static const u8 delays[] =
|
||||
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
|
||||
static const u8 totals[] =
|
||||
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 };
|
||||
# define NDELAY ArraySize(delays)
|
||||
sqlite3 *db = (sqlite3 *)ptr;
|
||||
int timeout = db->busyTimeout;
|
||||
int tmout = db->busyTimeout;
|
||||
int delay, prior;
|
||||
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
|
||||
if( count ){
|
||||
tmout = 0;
|
||||
sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pFile);
|
||||
#endif
|
||||
assert( count>=0 );
|
||||
if( count < NDELAY ){
|
||||
delay = delays[count];
|
||||
@@ -1482,16 +1540,19 @@ static int sqliteDefaultBusyCallback(
|
||||
delay = delays[NDELAY-1];
|
||||
prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
|
||||
}
|
||||
if( prior + delay > timeout ){
|
||||
delay = timeout - prior;
|
||||
if( prior + delay > tmout ){
|
||||
delay = tmout - prior;
|
||||
if( delay<=0 ) return 0;
|
||||
}
|
||||
sqlite3OsSleep(db->pVfs, delay*1000);
|
||||
return 1;
|
||||
#else
|
||||
/* This case for unix systems that lack usleep() support. Sleeping
|
||||
** must be done in increments of whole seconds */
|
||||
sqlite3 *db = (sqlite3 *)ptr;
|
||||
int timeout = ((sqlite3 *)ptr)->busyTimeout;
|
||||
if( (count+1)*1000 > timeout ){
|
||||
int tmout = ((sqlite3 *)ptr)->busyTimeout;
|
||||
UNUSED_PARAMETER(pFile);
|
||||
if( (count+1)*1000 > tmout ){
|
||||
return 0;
|
||||
}
|
||||
sqlite3OsSleep(db->pVfs, 1000000);
|
||||
@@ -1502,14 +1563,25 @@ static int sqliteDefaultBusyCallback(
|
||||
/*
|
||||
** Invoke the given busy handler.
|
||||
**
|
||||
** This routine is called when an operation failed with a lock.
|
||||
** This routine is called when an operation failed to acquire a
|
||||
** lock on VFS file pFile.
|
||||
**
|
||||
** If this routine returns non-zero, the lock is retried. If it
|
||||
** returns 0, the operation aborts with an SQLITE_BUSY error.
|
||||
*/
|
||||
int sqlite3InvokeBusyHandler(BusyHandler *p){
|
||||
int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
|
||||
int rc;
|
||||
if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0;
|
||||
rc = p->xFunc(p->pArg, p->nBusy);
|
||||
if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
|
||||
if( p->bExtraFileArg ){
|
||||
/* Add an extra parameter with the pFile pointer to the end of the
|
||||
** callback argument list */
|
||||
int (*xTra)(void*,int,sqlite3_file*);
|
||||
xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
|
||||
rc = xTra(p->pBusyArg, p->nBusy, pFile);
|
||||
}else{
|
||||
/* Legacy style busy handler callback */
|
||||
rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
|
||||
}
|
||||
if( rc==0 ){
|
||||
p->nBusy = -1;
|
||||
}else{
|
||||
@@ -1531,9 +1603,10 @@ int sqlite3_busy_handler(
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->busyHandler.xFunc = xBusy;
|
||||
db->busyHandler.pArg = pArg;
|
||||
db->busyHandler.xBusyHandler = xBusy;
|
||||
db->busyHandler.pBusyArg = pArg;
|
||||
db->busyHandler.nBusy = 0;
|
||||
db->busyHandler.bExtraFileArg = 0;
|
||||
db->busyTimeout = 0;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
@@ -1581,8 +1654,10 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
if( ms>0 ){
|
||||
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
|
||||
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
|
||||
(void*)db);
|
||||
db->busyTimeout = ms;
|
||||
db->busyHandler.bExtraFileArg = 1;
|
||||
}else{
|
||||
sqlite3_busy_handler(db, 0, 0);
|
||||
}
|
||||
@@ -1618,6 +1693,8 @@ int sqlite3CreateFunc(
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
|
||||
FuncDestructor *pDestructor
|
||||
){
|
||||
FuncDef *p;
|
||||
@@ -1625,12 +1702,14 @@ int sqlite3CreateFunc(
|
||||
int extraFlags;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( zFunctionName==0 ||
|
||||
(xSFunc && (xFinal || xStep)) ||
|
||||
(!xSFunc && (xFinal && !xStep)) ||
|
||||
(!xSFunc && (!xFinal && xStep)) ||
|
||||
(nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
|
||||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
|
||||
assert( xValue==0 || xSFunc==0 );
|
||||
if( zFunctionName==0 /* Must have a valid name */
|
||||
|| (xSFunc!=0 && xFinal!=0) /* Not both xSFunc and xFinal */
|
||||
|| ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */
|
||||
|| ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */
|
||||
|| (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG)
|
||||
|| (255<(nName = sqlite3Strlen30( zFunctionName)))
|
||||
){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
|
||||
@@ -1651,10 +1730,10 @@ int sqlite3CreateFunc(
|
||||
}else if( enc==SQLITE_ANY ){
|
||||
int rc;
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
|
||||
pUserData, xSFunc, xStep, xFinal, pDestructor);
|
||||
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
|
||||
pUserData, xSFunc, xStep, xFinal, pDestructor);
|
||||
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@@ -1671,14 +1750,14 @@ int sqlite3CreateFunc(
|
||||
** operation to continue but invalidate all precompiled statements.
|
||||
*/
|
||||
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
|
||||
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
|
||||
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==(u32)enc && p->nArg==nArg ){
|
||||
if( db->nVdbeActive ){
|
||||
sqlite3ErrorWithMsg(db, SQLITE_BUSY,
|
||||
"unable to delete/modify user-function due to active statements");
|
||||
assert( !db->mallocFailed );
|
||||
return SQLITE_BUSY;
|
||||
}else{
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1700,11 +1779,68 @@ int sqlite3CreateFunc(
|
||||
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
||||
p->xSFunc = xSFunc ? xSFunc : xStep;
|
||||
p->xFinalize = xFinal;
|
||||
p->xValue = xValue;
|
||||
p->xInverse = xInverse;
|
||||
p->pUserData = pUserData;
|
||||
p->nArg = (u16)nArg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Worker function used by utf-8 APIs that create new functions:
|
||||
**
|
||||
** sqlite3_create_function()
|
||||
** sqlite3_create_function_v2()
|
||||
** sqlite3_create_window_function()
|
||||
*/
|
||||
static int createFunctionApi(
|
||||
sqlite3 *db,
|
||||
const char *zFunc,
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInverse)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*)
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
FuncDestructor *pArg = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( xDestroy ){
|
||||
pArg = (FuncDestructor *)sqlite3Malloc(sizeof(FuncDestructor));
|
||||
if( !pArg ){
|
||||
sqlite3OomFault(db);
|
||||
xDestroy(p);
|
||||
goto out;
|
||||
}
|
||||
pArg->nRef = 0;
|
||||
pArg->xDestroy = xDestroy;
|
||||
pArg->pUserData = p;
|
||||
}
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p,
|
||||
xSFunc, xStep, xFinal, xValue, xInverse, pArg
|
||||
);
|
||||
if( pArg && pArg->nRef==0 ){
|
||||
assert( rc!=SQLITE_OK );
|
||||
xDestroy(p);
|
||||
sqlite3_free(pArg);
|
||||
}
|
||||
|
||||
out:
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create new user functions.
|
||||
*/
|
||||
@@ -1718,10 +1854,9 @@ int sqlite3_create_function(
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*)
|
||||
){
|
||||
return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
||||
xFinal, 0);
|
||||
return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
||||
xFinal, 0, 0, 0);
|
||||
}
|
||||
|
||||
int sqlite3_create_function_v2(
|
||||
sqlite3 *db,
|
||||
const char *zFunc,
|
||||
@@ -1733,35 +1868,23 @@ int sqlite3_create_function_v2(
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xDestroy)(void *)
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
FuncDestructor *pArg = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( xDestroy ){
|
||||
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
|
||||
if( !pArg ){
|
||||
xDestroy(p);
|
||||
goto out;
|
||||
}
|
||||
pArg->xDestroy = xDestroy;
|
||||
pArg->pUserData = p;
|
||||
}
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg);
|
||||
if( pArg && pArg->nRef==0 ){
|
||||
assert( rc!=SQLITE_OK );
|
||||
xDestroy(p);
|
||||
sqlite3DbFree(db, pArg);
|
||||
}
|
||||
|
||||
out:
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
||||
xFinal, 0, 0, xDestroy);
|
||||
}
|
||||
int sqlite3_create_window_function(
|
||||
sqlite3 *db,
|
||||
const char *zFunc,
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xDestroy)(void *)
|
||||
){
|
||||
return createFunctionApi(db, zFunc, nArg, enc, p, 0, xStep,
|
||||
xFinal, xValue, xInverse, xDestroy);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@@ -1784,7 +1907,7 @@ int sqlite3_create_function16(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0,0,0);
|
||||
sqlite3DbFree(db, zFunc8);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@@ -1793,6 +1916,28 @@ int sqlite3_create_function16(
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** The following is the implementation of an SQL function that always
|
||||
** fails with an error message stating that the function is used in the
|
||||
** wrong context. The sqlite3_overload_function() API might construct
|
||||
** SQL function that use this routine so that the functions will exist
|
||||
** for name resolution but are actually overloaded by the xFindFunction
|
||||
** method of virtual tables.
|
||||
*/
|
||||
static void sqlite3InvalidFunction(
|
||||
sqlite3_context *context, /* The function calling context */
|
||||
int NotUsed, /* Number of arguments to the function */
|
||||
sqlite3_value **NotUsed2 /* Value of each argument */
|
||||
){
|
||||
const char *zName = (const char*)sqlite3_user_data(context);
|
||||
char *zErr;
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
zErr = sqlite3_mprintf(
|
||||
"unable to use function %s in the requested context", zName);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Declare that a function has been overloaded by a virtual table.
|
||||
**
|
||||
@@ -1810,7 +1955,8 @@ int sqlite3_overload_function(
|
||||
const char *zName,
|
||||
int nArg
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int rc;
|
||||
char *zCopy;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 || nArg<-2 ){
|
||||
@@ -1818,13 +1964,13 @@ int sqlite3_overload_function(
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
0, sqlite3InvalidFunction, 0, 0, 0);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
if( rc ) return SQLITE_OK;
|
||||
zCopy = sqlite3_mprintf(zName);
|
||||
if( zCopy==0 ) return SQLITE_NOMEM;
|
||||
return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8,
|
||||
zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
@@ -2175,7 +2321,8 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
|
||||
** checkpointed. If an error is encountered it is returned immediately -
|
||||
** no attempt is made to checkpoint any remaining databases.
|
||||
**
|
||||
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
|
||||
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART
|
||||
** or TRUNCATE.
|
||||
*/
|
||||
int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
@@ -2385,7 +2532,7 @@ static int createCollation(
|
||||
"unable to delete/modify collation sequence due to active statements");
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
|
||||
/* If collation sequence pColl was created directly by a call to
|
||||
** sqlite3_create_collation, and not generated by synthCollSeq(),
|
||||
@@ -2821,6 +2968,7 @@ static int openDatabase(
|
||||
}else{
|
||||
isThreadsafe = sqlite3GlobalConfig.bFullMutex;
|
||||
}
|
||||
|
||||
if( flags & SQLITE_OPEN_PRIVATECACHE ){
|
||||
flags &= ~SQLITE_OPEN_SHAREDCACHE;
|
||||
}else if( sqlite3GlobalConfig.sharedCacheEnabled ){
|
||||
@@ -2853,13 +3001,20 @@ static int openDatabase(
|
||||
/* Allocate the sqlite data structure */
|
||||
db = sqlite3MallocZero( sizeof(sqlite3) );
|
||||
if( db==0 ) goto opendb_out;
|
||||
if( isThreadsafe ){
|
||||
if( isThreadsafe
|
||||
#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
||||
|| sqlite3GlobalConfig.bCoreMutex
|
||||
#endif
|
||||
){
|
||||
db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
if( db->mutex==0 ){
|
||||
sqlite3_free(db);
|
||||
db = 0;
|
||||
goto opendb_out;
|
||||
}
|
||||
if( isThreadsafe==0 ){
|
||||
sqlite3MutexWarnOnContention(db->mutex);
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->errMask = 0xff;
|
||||
@@ -3041,7 +3196,7 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3IcuInit(db);
|
||||
}
|
||||
@@ -3053,6 +3208,12 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DBPAGE_VTAB
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3DbpageRegister(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3DbstatRegister(db);
|
||||
@@ -3337,37 +3498,37 @@ int sqlite3_get_autocommit(sqlite3 *db){
|
||||
** 2. Invoke sqlite3_log() to provide the source code location where
|
||||
** a low-level error is first detected.
|
||||
*/
|
||||
static int reportError(int iErr, int lineno, const char *zType){
|
||||
int sqlite3ReportError(int iErr, int lineno, const char *zType){
|
||||
sqlite3_log(iErr, "%s at line %d of [%.10s]",
|
||||
zType, lineno, 20+sqlite3_sourceid());
|
||||
return iErr;
|
||||
}
|
||||
int sqlite3CorruptError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_CORRUPT, lineno, "database corruption");
|
||||
return sqlite3ReportError(SQLITE_CORRUPT, lineno, "database corruption");
|
||||
}
|
||||
int sqlite3MisuseError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_MISUSE, lineno, "misuse");
|
||||
return sqlite3ReportError(SQLITE_MISUSE, lineno, "misuse");
|
||||
}
|
||||
int sqlite3CantopenError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
||||
return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3CorruptPgnoError(int lineno, Pgno pgno){
|
||||
char zMsg[100];
|
||||
sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno);
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_CORRUPT, lineno, zMsg);
|
||||
return sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
|
||||
}
|
||||
int sqlite3NomemError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_NOMEM, lineno, "OOM");
|
||||
return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM");
|
||||
}
|
||||
int sqlite3IoerrnomemError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
|
||||
return sqlite3ReportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3560,10 +3721,11 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
|
||||
}else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
|
||||
*(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
|
||||
rc = SQLITE_OK;
|
||||
}else if( fd->pMethods ){
|
||||
rc = sqlite3OsFileControl(fd, op, pArg);
|
||||
}else if( op==SQLITE_FCNTL_DATA_VERSION ){
|
||||
*(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = SQLITE_NOTFOUND;
|
||||
rc = sqlite3OsFileControl(fd, op, pArg);
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
@@ -3712,7 +3874,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** This action provides a run-time test to see how the ALWAYS and
|
||||
** NEVER macros were defined at compile-time.
|
||||
**
|
||||
** The return value is ALWAYS(X).
|
||||
** The return value is ALWAYS(X) if X is true, or 0 if X is false.
|
||||
**
|
||||
** The recommended test is X==2. If the return value is 2, that means
|
||||
** ALWAYS() and NEVER() are both no-op pass-through macros, which is the
|
||||
@@ -3735,7 +3897,7 @@ int sqlite3_test_control(int op, ...){
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ALWAYS: {
|
||||
int x = va_arg(ap,int);
|
||||
rc = ALWAYS(x);
|
||||
rc = x ? ALWAYS(x) : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3784,40 +3946,6 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_N_KEYWORD
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
|
||||
**
|
||||
** If zWord is a keyword recognized by the parser, then return the
|
||||
** number of keywords. Or if zWord is not a keyword, return 0.
|
||||
**
|
||||
** This test feature is only available in the amalgamation since
|
||||
** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite
|
||||
** is built using separate source files.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ISKEYWORD: {
|
||||
const char *zWord = va_arg(ap, const char*);
|
||||
int n = sqlite3Strlen30(zWord);
|
||||
rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
|
||||
**
|
||||
** Pass pFree into sqlite3ScratchFree().
|
||||
** If sz>0 then allocate a scratch buffer into pNew.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_SCRATCHMALLOC: {
|
||||
void *pFree, **ppNew;
|
||||
int sz;
|
||||
sz = va_arg(ap, int);
|
||||
ppNew = va_arg(ap, void**);
|
||||
pFree = va_arg(ap, void*);
|
||||
if( sz ) *ppNew = sqlite3ScratchMalloc(sz);
|
||||
sqlite3ScratchFree(pFree);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
|
||||
**
|
||||
** If parameter onoff is non-zero, configure the wrappers so that all
|
||||
@@ -3859,7 +3987,8 @@ int sqlite3_test_control(int op, ...){
|
||||
*/
|
||||
case SQLITE_TESTCTRL_VDBE_COVERAGE: {
|
||||
#ifdef SQLITE_VDBE_COVERAGE
|
||||
typedef void (*branch_callback)(void*,int,u8,u8);
|
||||
typedef void (*branch_callback)(void*,unsigned int,
|
||||
unsigned char,unsigned char);
|
||||
sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback);
|
||||
sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*);
|
||||
#endif
|
||||
@@ -3911,6 +4040,22 @@ int sqlite3_test_control(int op, ...){
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(YYCOVERAGE)
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_PARSER_COVERAGE, FILE *out)
|
||||
**
|
||||
** This test control (only available when SQLite is compiled with
|
||||
** -DYYCOVERAGE) writes a report onto "out" that shows all
|
||||
** state/lookahead combinations in the parser state machine
|
||||
** which are never exercised. If any state is missed, make the
|
||||
** return code SQLITE_ERROR.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PARSER_COVERAGE: {
|
||||
FILE *out = va_arg(ap, FILE*);
|
||||
if( sqlite3ParserCoverage(out) ) rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
#endif /* defined(YYCOVERAGE) */
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_UNTESTABLE */
|
||||
@@ -3959,7 +4104,7 @@ sqlite3_int64 sqlite3_uri_int64(
|
||||
){
|
||||
const char *z = sqlite3_uri_parameter(zFilename, zParam);
|
||||
sqlite3_int64 v;
|
||||
if( z && sqlite3DecOrHexToI64(z, &v)==SQLITE_OK ){
|
||||
if( z && sqlite3DecOrHexToI64(z, &v)==0 ){
|
||||
bDflt = v;
|
||||
}
|
||||
return bDflt;
|
||||
@@ -4030,7 +4175,7 @@ int sqlite3_snapshot_get(
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
}
|
||||
@@ -4065,11 +4210,29 @@ int sqlite3_snapshot_open(
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot);
|
||||
if( sqlite3BtreeIsInTrans(pBt)==0 ){
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
int bUnlock = 0;
|
||||
if( sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
if( db->nVdbeActive==0 ){
|
||||
rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot);
|
||||
if( rc==SQLITE_OK ){
|
||||
bUnlock = 1;
|
||||
rc = sqlite3BtreeCommit(pBt);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
|
||||
rc = sqlite3PagerSnapshotOpen(pPager, pSnapshot);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
sqlite3PagerSnapshotOpen(pPager, 0);
|
||||
}
|
||||
if( bUnlock ){
|
||||
sqlite3PagerSnapshotUnlock(pPager);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4100,7 +4263,7 @@ int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeCommit(pBt);
|
||||
|
||||
+21
-148
@@ -32,14 +32,6 @@ int sqlite3_release_memory(int n){
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** An instance of the following object records the location of
|
||||
** each unused scratch buffer.
|
||||
*/
|
||||
typedef struct ScratchFreeslot {
|
||||
struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
|
||||
} ScratchFreeslot;
|
||||
|
||||
/*
|
||||
** State information local to the memory allocation subsystem.
|
||||
*/
|
||||
@@ -47,22 +39,12 @@ static SQLITE_WSD struct Mem0Global {
|
||||
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
||||
sqlite3_int64 alarmThreshold; /* The soft heap limit */
|
||||
|
||||
/*
|
||||
** Pointers to the end of sqlite3GlobalConfig.pScratch memory
|
||||
** (so that a range test can be used to determine if an allocation
|
||||
** being freed came from pScratch) and a pointer to the list of
|
||||
** unused scratch allocations.
|
||||
*/
|
||||
void *pScratchEnd;
|
||||
ScratchFreeslot *pScratchFree;
|
||||
u32 nScratchFree;
|
||||
|
||||
/*
|
||||
** True if heap is nearly "full" where "full" is defined by the
|
||||
** sqlite3_soft_heap_limit() setting.
|
||||
*/
|
||||
int nearlyFull;
|
||||
} mem0 = { 0, 0, 0, 0, 0, 0 };
|
||||
} mem0 = { 0, 0, 0 };
|
||||
|
||||
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
||||
|
||||
@@ -132,28 +114,6 @@ int sqlite3MallocInit(void){
|
||||
}
|
||||
memset(&mem0, 0, sizeof(mem0));
|
||||
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
||||
&& sqlite3GlobalConfig.nScratch>0 ){
|
||||
int i, n, sz;
|
||||
ScratchFreeslot *pSlot;
|
||||
sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
|
||||
sqlite3GlobalConfig.szScratch = sz;
|
||||
pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
|
||||
n = sqlite3GlobalConfig.nScratch;
|
||||
mem0.pScratchFree = pSlot;
|
||||
mem0.nScratchFree = n;
|
||||
for(i=0; i<n-1; i++){
|
||||
pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
|
||||
pSlot = pSlot->pNext;
|
||||
}
|
||||
pSlot->pNext = 0;
|
||||
mem0.pScratchEnd = (void*)&pSlot[1];
|
||||
}else{
|
||||
mem0.pScratchEnd = 0;
|
||||
sqlite3GlobalConfig.pScratch = 0;
|
||||
sqlite3GlobalConfig.szScratch = 0;
|
||||
sqlite3GlobalConfig.nScratch = 0;
|
||||
}
|
||||
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|
||||
|| sqlite3GlobalConfig.nPage<=0 ){
|
||||
sqlite3GlobalConfig.pPage = 0;
|
||||
@@ -304,105 +264,6 @@ void *sqlite3_malloc64(sqlite3_uint64 n){
|
||||
return sqlite3Malloc(n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Each thread may only have a single outstanding allocation from
|
||||
** xScratchMalloc(). We verify this constraint in the single-threaded
|
||||
** case by setting scratchAllocOut to 1 when an allocation
|
||||
** is outstanding clearing it when the allocation is freed.
|
||||
*/
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
static int scratchAllocOut = 0;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Allocate memory that is to be used and released right away.
|
||||
** This routine is similar to alloca() in that it is not intended
|
||||
** for situations where the memory might be held long-term. This
|
||||
** routine is intended to get memory to old large transient data
|
||||
** structures that would not normally fit on the stack of an
|
||||
** embedded processor.
|
||||
*/
|
||||
void *sqlite3ScratchMalloc(int n){
|
||||
void *p;
|
||||
assert( n>0 );
|
||||
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
||||
p = mem0.pScratchFree;
|
||||
mem0.pScratchFree = mem0.pScratchFree->pNext;
|
||||
mem0.nScratchFree--;
|
||||
sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
p = sqlite3Malloc(n);
|
||||
if( sqlite3GlobalConfig.bMemstat && p ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p));
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
||||
}
|
||||
assert( sqlite3_mutex_notheld(mem0.mutex) );
|
||||
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch
|
||||
** buffers per thread.
|
||||
**
|
||||
** This can only be checked in single-threaded mode.
|
||||
*/
|
||||
assert( scratchAllocOut==0 );
|
||||
if( p ) scratchAllocOut++;
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
void sqlite3ScratchFree(void *p){
|
||||
if( p ){
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* Verify that no more than two scratch allocation per thread
|
||||
** is outstanding at one time. (This is only checked in the
|
||||
** single-threaded case since checking in the multi-threaded case
|
||||
** would be much more complicated.) */
|
||||
assert( scratchAllocOut>=1 && scratchAllocOut<=2 );
|
||||
scratchAllocOut--;
|
||||
#endif
|
||||
|
||||
if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){
|
||||
/* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
|
||||
ScratchFreeslot *pSlot;
|
||||
pSlot = (ScratchFreeslot*)p;
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
pSlot->pNext = mem0.pScratchFree;
|
||||
mem0.pScratchFree = pSlot;
|
||||
mem0.nScratchFree++;
|
||||
assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch );
|
||||
sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
/* Release memory back to the heap */
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
|
||||
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
int iSize = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize);
|
||||
sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize);
|
||||
sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** TRUE if p is a lookaside memory allocation from db
|
||||
*/
|
||||
@@ -493,7 +354,6 @@ void sqlite3DbFreeNN(sqlite3 *db, void *p){
|
||||
#endif
|
||||
pBuf->pNext = db->lookaside.pFree;
|
||||
db->lookaside.pFree = pBuf;
|
||||
db->lookaside.nOut--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -654,16 +514,16 @@ void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
|
||||
assert( db->mallocFailed==0 );
|
||||
if( n>db->lookaside.sz ){
|
||||
db->lookaside.anStat[1]++;
|
||||
}else if( (pBuf = db->lookaside.pFree)==0 ){
|
||||
db->lookaside.anStat[2]++;
|
||||
}else{
|
||||
}else if( (pBuf = db->lookaside.pFree)!=0 ){
|
||||
db->lookaside.pFree = pBuf->pNext;
|
||||
db->lookaside.nOut++;
|
||||
db->lookaside.anStat[0]++;
|
||||
if( db->lookaside.nOut>db->lookaside.mxOut ){
|
||||
db->lookaside.mxOut = db->lookaside.nOut;
|
||||
}
|
||||
return (void*)pBuf;
|
||||
}else if( (pBuf = db->lookaside.pInit)!=0 ){
|
||||
db->lookaside.pInit = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else{
|
||||
db->lookaside.anStat[2]++;
|
||||
}
|
||||
}else if( db->mallocFailed ){
|
||||
return 0;
|
||||
@@ -767,6 +627,19 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** The text between zStart and zEnd represents a phrase within a larger
|
||||
** SQL statement. Make a copy of this phrase in space obtained form
|
||||
** sqlite3DbMalloc(). Omit leading and trailing whitespace.
|
||||
*/
|
||||
char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
||||
int n;
|
||||
while( sqlite3Isspace(zStart[0]) ) zStart++;
|
||||
n = (int)(zEnd - zStart);
|
||||
while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
|
||||
return sqlite3DbStrNDup(db, zStart, n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free any prior content in *pz and replace it with a copy of zNew.
|
||||
*/
|
||||
|
||||
+589
@@ -0,0 +1,589 @@
|
||||
/*
|
||||
** 2016-09-07
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file implements an in-memory VFS. A database is held as a contiguous
|
||||
** block of memory.
|
||||
**
|
||||
** This file also implements interface sqlite3_serialize() and
|
||||
** sqlite3_deserialize().
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this utility
|
||||
*/
|
||||
typedef struct sqlite3_vfs MemVfs;
|
||||
typedef struct MemFile MemFile;
|
||||
|
||||
/* Access to a lower-level VFS that (might) implement dynamic loading,
|
||||
** access to randomness, etc.
|
||||
*/
|
||||
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
||||
|
||||
/* An open file */
|
||||
struct MemFile {
|
||||
sqlite3_file base; /* IO methods */
|
||||
sqlite3_int64 sz; /* Size of the file */
|
||||
sqlite3_int64 szMax; /* Space allocated to aData */
|
||||
unsigned char *aData; /* content of the file */
|
||||
int nMmap; /* Number of memory mapped pages */
|
||||
unsigned mFlags; /* Flags */
|
||||
int eLock; /* Most recent lock against this file */
|
||||
};
|
||||
|
||||
/*
|
||||
** Methods for MemFile
|
||||
*/
|
||||
static int memdbClose(sqlite3_file*);
|
||||
static int memdbRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int memdbWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int memdbTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int memdbSync(sqlite3_file*, int flags);
|
||||
static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int memdbLock(sqlite3_file*, int);
|
||||
/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */
|
||||
static int memdbFileControl(sqlite3_file*, int op, void *pArg);
|
||||
/* static int memdbSectorSize(sqlite3_file*); // not used */
|
||||
static int memdbDeviceCharacteristics(sqlite3_file*);
|
||||
static int memdbFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
||||
static int memdbUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
||||
|
||||
/*
|
||||
** Methods for MemVfs
|
||||
*/
|
||||
static int memdbOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
/* static int memdbDelete(sqlite3_vfs*, const char *zName, int syncDir); */
|
||||
static int memdbAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int memdbFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
static void *memdbDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void memdbDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||||
static void memdbDlClose(sqlite3_vfs*, void*);
|
||||
static int memdbRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int memdbSleep(sqlite3_vfs*, int microseconds);
|
||||
/* static int memdbCurrentTime(sqlite3_vfs*, double*); */
|
||||
static int memdbGetLastError(sqlite3_vfs*, int, char *);
|
||||
static int memdbCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
|
||||
static sqlite3_vfs memdb_vfs = {
|
||||
2, /* iVersion */
|
||||
0, /* szOsFile (set when registered) */
|
||||
1024, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"memdb", /* zName */
|
||||
0, /* pAppData (set when registered) */
|
||||
memdbOpen, /* xOpen */
|
||||
0, /* memdbDelete, */ /* xDelete */
|
||||
memdbAccess, /* xAccess */
|
||||
memdbFullPathname, /* xFullPathname */
|
||||
memdbDlOpen, /* xDlOpen */
|
||||
memdbDlError, /* xDlError */
|
||||
memdbDlSym, /* xDlSym */
|
||||
memdbDlClose, /* xDlClose */
|
||||
memdbRandomness, /* xRandomness */
|
||||
memdbSleep, /* xSleep */
|
||||
0, /* memdbCurrentTime, */ /* xCurrentTime */
|
||||
memdbGetLastError, /* xGetLastError */
|
||||
memdbCurrentTimeInt64 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static const sqlite3_io_methods memdb_io_methods = {
|
||||
3, /* iVersion */
|
||||
memdbClose, /* xClose */
|
||||
memdbRead, /* xRead */
|
||||
memdbWrite, /* xWrite */
|
||||
memdbTruncate, /* xTruncate */
|
||||
memdbSync, /* xSync */
|
||||
memdbFileSize, /* xFileSize */
|
||||
memdbLock, /* xLock */
|
||||
memdbLock, /* xUnlock - same as xLock in this case */
|
||||
0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */
|
||||
memdbFileControl, /* xFileControl */
|
||||
0, /* memdbSectorSize,*/ /* xSectorSize */
|
||||
memdbDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
0, /* xShmMap */
|
||||
0, /* xShmLock */
|
||||
0, /* xShmBarrier */
|
||||
0, /* xShmUnmap */
|
||||
memdbFetch, /* xFetch */
|
||||
memdbUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Close an memdb-file.
|
||||
**
|
||||
** The pData pointer is owned by the application, so there is nothing
|
||||
** to free.
|
||||
*/
|
||||
static int memdbClose(sqlite3_file *pFile){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from an memdb-file.
|
||||
*/
|
||||
static int memdbRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
if( iOfst+iAmt>p->sz ){
|
||||
memset(zBuf, 0, iAmt);
|
||||
if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
memcpy(zBuf, p->aData+iOfst, iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to enlarge the memory allocation to hold at least sz bytes
|
||||
*/
|
||||
static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){
|
||||
unsigned char *pNew;
|
||||
if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
pNew = sqlite3_realloc64(p->aData, newSz);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
p->aData = pNew;
|
||||
p->szMax = newSz;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an memdb-file.
|
||||
*/
|
||||
static int memdbWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *z,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
if( iOfst+iAmt>p->sz ){
|
||||
int rc;
|
||||
if( iOfst+iAmt>p->szMax
|
||||
&& (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK
|
||||
){
|
||||
return rc;
|
||||
}
|
||||
if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
|
||||
p->sz = iOfst+iAmt;
|
||||
}
|
||||
memcpy(p->aData+iOfst, z, iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an memdb-file.
|
||||
**
|
||||
** In rollback mode (which is always the case for memdb, as it does not
|
||||
** support WAL mode) the truncate() method is only used to reduce
|
||||
** the size of a file, never to increase the size.
|
||||
*/
|
||||
static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
if( NEVER(size>p->sz) ) return SQLITE_FULL;
|
||||
p->sz = size;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync an memdb-file.
|
||||
*/
|
||||
static int memdbSync(sqlite3_file *pFile, int flags){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of an memdb-file.
|
||||
*/
|
||||
static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
*pSize = p->sz;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock an memdb-file.
|
||||
*/
|
||||
static int memdbLock(sqlite3_file *pFile, int eLock){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
p->eLock = eLock;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#if 0 /* Never used because memdbAccess() always returns false */
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on an memdb-file.
|
||||
*/
|
||||
static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
*pResOut = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on an memdb-file.
|
||||
*/
|
||||
static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
int rc = SQLITE_NOTFOUND;
|
||||
if( op==SQLITE_FCNTL_VFSNAME ){
|
||||
*(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
||||
/*
|
||||
** Return the sector-size in bytes for an memdb-file.
|
||||
*/
|
||||
static int memdbSectorSize(sqlite3_file *pFile){
|
||||
return 1024;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an memdb-file.
|
||||
*/
|
||||
static int memdbDeviceCharacteristics(sqlite3_file *pFile){
|
||||
return SQLITE_IOCAP_ATOMIC |
|
||||
SQLITE_IOCAP_POWERSAFE_OVERWRITE |
|
||||
SQLITE_IOCAP_SAFE_APPEND |
|
||||
SQLITE_IOCAP_SEQUENTIAL;
|
||||
}
|
||||
|
||||
/* Fetch a page of a memory-mapped file */
|
||||
static int memdbFetch(
|
||||
sqlite3_file *pFile,
|
||||
sqlite3_int64 iOfst,
|
||||
int iAmt,
|
||||
void **pp
|
||||
){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
p->nMmap++;
|
||||
*pp = (void*)(p->aData + iOfst);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
p->nMmap--;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an mem file handle.
|
||||
*/
|
||||
static int memdbOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
MemFile *p = (MemFile*)pFile;
|
||||
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
||||
return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags);
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
|
||||
assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */
|
||||
*pOutFlags = flags | SQLITE_OPEN_MEMORY;
|
||||
p->base.pMethods = &memdb_io_methods;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#if 0 /* Only used to delete rollback journals, master journals, and WAL
|
||||
** files, none of which exist in memdb. So this routine is never used */
|
||||
/*
|
||||
** Delete the file located at zPath. If the dirSync argument is true,
|
||||
** ensure the file-system modifications are synced to disk before
|
||||
** returning.
|
||||
*/
|
||||
static int memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return SQLITE_IOERR_DELETE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Test for access permissions. Return true if the requested permission
|
||||
** is available, or false otherwise.
|
||||
**
|
||||
** With memdb, no files ever exist on disk. So always return false.
|
||||
*/
|
||||
static int memdbAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
*pResOut = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate buffer zOut with the full canonical pathname corresponding
|
||||
** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
||||
** of at least (INST_MAX_PATHNAME+1) bytes.
|
||||
*/
|
||||
static int memdbFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open the dynamic library located at zPath and return a handle.
|
||||
*/
|
||||
static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
||||
** utf-8 string describing the most recent error encountered associated
|
||||
** with dynamic libraries.
|
||||
*/
|
||||
static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
||||
*/
|
||||
static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the dynamic library handle pHandle.
|
||||
*/
|
||||
static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
||||
** random data.
|
||||
*/
|
||||
static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for nMicro microseconds. Return the number of microseconds
|
||||
** actually slept.
|
||||
*/
|
||||
static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
||||
}
|
||||
|
||||
#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */
|
||||
/*
|
||||
** Return the current time as a Julian Day number in *pTimeOut.
|
||||
*/
|
||||
static int memdbCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int memdbGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
||||
}
|
||||
static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate a database connection pointer and schema name into a
|
||||
** MemFile pointer.
|
||||
*/
|
||||
static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){
|
||||
MemFile *p = 0;
|
||||
int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
|
||||
if( rc ) return 0;
|
||||
if( p->base.pMethods!=&memdb_io_methods ) return 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the serialization of a database
|
||||
*/
|
||||
unsigned char *sqlite3_serialize(
|
||||
sqlite3 *db, /* The database connection */
|
||||
const char *zSchema, /* Which database within the connection */
|
||||
sqlite3_int64 *piSize, /* Write size here, if not NULL */
|
||||
unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */
|
||||
){
|
||||
MemFile *p;
|
||||
int iDb;
|
||||
Btree *pBt;
|
||||
sqlite3_int64 sz;
|
||||
int szPage = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
unsigned char *pOut;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
||||
p = memdbFromDbSchema(db, zSchema);
|
||||
iDb = sqlite3FindDbName(db, zSchema);
|
||||
if( piSize ) *piSize = -1;
|
||||
if( iDb<0 ) return 0;
|
||||
if( p ){
|
||||
if( piSize ) *piSize = p->sz;
|
||||
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
||||
pOut = p->aData;
|
||||
}else{
|
||||
pOut = sqlite3_malloc64( p->sz );
|
||||
if( pOut ) memcpy(pOut, p->aData, p->sz);
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
pBt = db->aDb[iDb].pBt;
|
||||
if( pBt==0 ) return 0;
|
||||
szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema);
|
||||
rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM;
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) return 0;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
pOut = 0;
|
||||
}else{
|
||||
sz = sqlite3_column_int64(pStmt, 0)*szPage;
|
||||
if( piSize ) *piSize = sz;
|
||||
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
||||
pOut = 0;
|
||||
}else{
|
||||
pOut = sqlite3_malloc64( sz );
|
||||
if( pOut ){
|
||||
int nPage = sqlite3_column_int(pStmt, 0);
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
int pgno;
|
||||
for(pgno=1; pgno<=nPage; pgno++){
|
||||
DbPage *pPage = 0;
|
||||
unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1);
|
||||
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pTo, sqlite3PagerGetData(pPage), szPage);
|
||||
}else{
|
||||
memset(pTo, 0, szPage);
|
||||
}
|
||||
sqlite3PagerUnref(pPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* Convert zSchema to a MemDB and initialize its content.
|
||||
*/
|
||||
int sqlite3_deserialize(
|
||||
sqlite3 *db, /* The database connection */
|
||||
const char *zSchema, /* Which DB to reopen with the deserialization */
|
||||
unsigned char *pData, /* The serialized database content */
|
||||
sqlite3_int64 szDb, /* Number bytes in the deserialization */
|
||||
sqlite3_int64 szBuf, /* Total size of buffer pData[] */
|
||||
unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */
|
||||
){
|
||||
MemFile *p;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc;
|
||||
int iDb;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
if( szDb<0 ) return SQLITE_MISUSE_BKPT;
|
||||
if( szBuf<0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
||||
iDb = sqlite3FindDbName(db, zSchema);
|
||||
if( iDb<0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto end_deserialize;
|
||||
}
|
||||
zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) goto end_deserialize;
|
||||
db->init.iDb = (u8)iDb;
|
||||
db->init.reopenMemdb = 1;
|
||||
rc = sqlite3_step(pStmt);
|
||||
db->init.reopenMemdb = 0;
|
||||
if( rc!=SQLITE_DONE ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto end_deserialize;
|
||||
}
|
||||
p = memdbFromDbSchema(db, zSchema);
|
||||
if( p==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p->aData = pData;
|
||||
p->sz = szDb;
|
||||
p->szMax = szBuf;
|
||||
p->mFlags = mFlags;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
end_deserialize:
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called when the extension is loaded.
|
||||
** Register the new VFS.
|
||||
*/
|
||||
int sqlite3MemdbInit(void){
|
||||
sqlite3_vfs *pLower = sqlite3_vfs_find(0);
|
||||
int sz = pLower->szOsFile;
|
||||
memdb_vfs.pAppData = pLower;
|
||||
/* In all known configurations of SQLite, the size of a default
|
||||
** sqlite3_file is greater than the size of a memdb sqlite3_file.
|
||||
** Should that ever change, remove the following NEVER() */
|
||||
if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile);
|
||||
memdb_vfs.szOsFile = sz;
|
||||
return sqlite3_vfs_register(&memdb_vfs, 0);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_DESERIALIZE */
|
||||
+24
-8
@@ -96,7 +96,8 @@ static int memjrnlRead(
|
||||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
if( (iAmt+iOfst)>p->endpoint.iOffset ){
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
@@ -215,7 +216,8 @@ static int memjrnlWrite(
|
||||
** atomic-write optimization. In this case the first 28 bytes of the
|
||||
** journal file may be written as part of committing the transaction. */
|
||||
assert( iOfst==p->endpoint.iOffset || iOfst==0 );
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
if( iOfst==0 && p->pFirst ){
|
||||
assert( p->nChunkSize>iAmt );
|
||||
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
|
||||
@@ -384,17 +386,31 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
/*
|
||||
** If the argument p points to a MemJournal structure that is not an
|
||||
** in-memory-only journal file (i.e. is one that was opened with a +ve
|
||||
** nSpill parameter), and the underlying file has not yet been created,
|
||||
** create it now.
|
||||
** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
|
||||
** file has not yet been created, create it now.
|
||||
*/
|
||||
int sqlite3JournalCreate(sqlite3_file *p){
|
||||
int sqlite3JournalCreate(sqlite3_file *pJfd){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
|
||||
rc = memjrnlCreateFile((MemJournal*)p);
|
||||
MemJournal *p = (MemJournal*)pJfd;
|
||||
if( p->pMethod==&MemJournalMethods && (
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
p->nSpill>0
|
||||
#else
|
||||
/* While this appears to not be possible without ATOMIC_WRITE, the
|
||||
** paths are complex, so it seems prudent to leave the test in as
|
||||
** a NEVER(), in case our analysis is subtly flawed. */
|
||||
NEVER(p->nSpill>0)
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
|| (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
|
||||
#endif
|
||||
)){
|
||||
rc = memjrnlCreateFile(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+191
@@ -26,6 +26,193 @@ static SQLITE_WSD int mutexIsInit = 0;
|
||||
|
||||
|
||||
#ifndef SQLITE_MUTEX_OMIT
|
||||
|
||||
#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
||||
/*
|
||||
** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains
|
||||
** the implementation of a wrapper around the system default mutex
|
||||
** implementation (sqlite3DefaultMutex()).
|
||||
**
|
||||
** Most calls are passed directly through to the underlying default
|
||||
** mutex implementation. Except, if a mutex is configured by calling
|
||||
** sqlite3MutexWarnOnContention() on it, then if contention is ever
|
||||
** encountered within xMutexEnter() a warning is emitted via sqlite3_log().
|
||||
**
|
||||
** This type of mutex is used as the database handle mutex when testing
|
||||
** apps that usually use SQLITE_CONFIG_MULTITHREAD mode.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS
|
||||
** is defined. Variable CheckMutex.mutex is a pointer to the real mutex
|
||||
** allocated by the system mutex implementation. Variable iType is usually set
|
||||
** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST
|
||||
** or one of the static mutex identifiers. Or, if this is a recursive mutex
|
||||
** that has been configured using sqlite3MutexWarnOnContention(), it is
|
||||
** set to SQLITE_MUTEX_WARNONCONTENTION.
|
||||
*/
|
||||
typedef struct CheckMutex CheckMutex;
|
||||
struct CheckMutex {
|
||||
int iType;
|
||||
sqlite3_mutex *mutex;
|
||||
};
|
||||
|
||||
#define SQLITE_MUTEX_WARNONCONTENTION (-1)
|
||||
|
||||
/*
|
||||
** Pointer to real mutex methods object used by the CheckMutex
|
||||
** implementation. Set by checkMutexInit().
|
||||
*/
|
||||
static SQLITE_WSD const sqlite3_mutex_methods *pGlobalMutexMethods;
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int checkMutexHeld(sqlite3_mutex *p){
|
||||
return pGlobalMutexMethods->xMutexHeld(((CheckMutex*)p)->mutex);
|
||||
}
|
||||
static int checkMutexNotheld(sqlite3_mutex *p){
|
||||
return pGlobalMutexMethods->xMutexNotheld(((CheckMutex*)p)->mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static int checkMutexInit(void){
|
||||
pGlobalMutexMethods = sqlite3DefaultMutex();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int checkMutexEnd(void){
|
||||
pGlobalMutexMethods = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a mutex.
|
||||
*/
|
||||
static sqlite3_mutex *checkMutexAlloc(int iType){
|
||||
static CheckMutex staticMutexes[] = {
|
||||
{2, 0}, {3, 0}, {4, 0}, {5, 0},
|
||||
{6, 0}, {7, 0}, {8, 0}, {9, 0},
|
||||
{10, 0}, {11, 0}, {12, 0}, {13, 0}
|
||||
};
|
||||
CheckMutex *p = 0;
|
||||
|
||||
assert( SQLITE_MUTEX_RECURSIVE==1 && SQLITE_MUTEX_FAST==0 );
|
||||
if( iType<2 ){
|
||||
p = sqlite3MallocZero(sizeof(CheckMutex));
|
||||
if( p==0 ) return 0;
|
||||
p->iType = iType;
|
||||
}else{
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( iType-2>=ArraySize(staticMutexes) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
p = &staticMutexes[iType-2];
|
||||
}
|
||||
|
||||
if( p->mutex==0 ){
|
||||
p->mutex = pGlobalMutexMethods->xMutexAlloc(iType);
|
||||
if( p->mutex==0 ){
|
||||
if( iType<2 ){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (sqlite3_mutex*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a mutex.
|
||||
*/
|
||||
static void checkMutexFree(sqlite3_mutex *p){
|
||||
assert( SQLITE_MUTEX_RECURSIVE<2 );
|
||||
assert( SQLITE_MUTEX_FAST<2 );
|
||||
assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
|
||||
|
||||
#if SQLITE_ENABLE_API_ARMOR
|
||||
if( ((CheckMutex*)p)->iType<2 )
|
||||
#endif
|
||||
{
|
||||
CheckMutex *pCheck = (CheckMutex*)p;
|
||||
pGlobalMutexMethods->xMutexFree(pCheck->mutex);
|
||||
sqlite3_free(pCheck);
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
else{
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter the mutex.
|
||||
*/
|
||||
static void checkMutexEnter(sqlite3_mutex *p){
|
||||
CheckMutex *pCheck = (CheckMutex*)p;
|
||||
if( pCheck->iType==SQLITE_MUTEX_WARNONCONTENTION ){
|
||||
if( SQLITE_OK==pGlobalMutexMethods->xMutexTry(pCheck->mutex) ){
|
||||
return;
|
||||
}
|
||||
sqlite3_log(SQLITE_MISUSE,
|
||||
"illegal multi-threaded access to database connection"
|
||||
);
|
||||
}
|
||||
pGlobalMutexMethods->xMutexEnter(pCheck->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter the mutex (do not block).
|
||||
*/
|
||||
static int checkMutexTry(sqlite3_mutex *p){
|
||||
CheckMutex *pCheck = (CheckMutex*)p;
|
||||
return pGlobalMutexMethods->xMutexTry(pCheck->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
** Leave the mutex.
|
||||
*/
|
||||
static void checkMutexLeave(sqlite3_mutex *p){
|
||||
CheckMutex *pCheck = (CheckMutex*)p;
|
||||
pGlobalMutexMethods->xMutexLeave(pCheck->mutex);
|
||||
}
|
||||
|
||||
sqlite3_mutex_methods const *multiThreadedCheckMutex(void){
|
||||
static const sqlite3_mutex_methods sMutex = {
|
||||
checkMutexInit,
|
||||
checkMutexEnd,
|
||||
checkMutexAlloc,
|
||||
checkMutexFree,
|
||||
checkMutexEnter,
|
||||
checkMutexTry,
|
||||
checkMutexLeave,
|
||||
#ifdef SQLITE_DEBUG
|
||||
checkMutexHeld,
|
||||
checkMutexNotheld
|
||||
#else
|
||||
0,
|
||||
0
|
||||
#endif
|
||||
};
|
||||
return &sMutex;
|
||||
}
|
||||
|
||||
/*
|
||||
** Mark the SQLITE_MUTEX_RECURSIVE mutex passed as the only argument as
|
||||
** one on which there should be no contention.
|
||||
*/
|
||||
void sqlite3MutexWarnOnContention(sqlite3_mutex *p){
|
||||
if( sqlite3GlobalConfig.mutex.xMutexAlloc==checkMutexAlloc ){
|
||||
CheckMutex *pCheck = (CheckMutex*)p;
|
||||
assert( pCheck->iType==SQLITE_MUTEX_RECURSIVE );
|
||||
pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION;
|
||||
}
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */
|
||||
|
||||
/*
|
||||
** Initialize the mutex system.
|
||||
*/
|
||||
@@ -41,7 +228,11 @@ int sqlite3MutexInit(void){
|
||||
sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex;
|
||||
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
||||
pFrom = multiThreadedCheckMutex();
|
||||
#else
|
||||
pFrom = sqlite3DefaultMutex();
|
||||
#endif
|
||||
}else{
|
||||
pFrom = sqlite3NoopMutex();
|
||||
}
|
||||
|
||||
+23
-16
@@ -50,11 +50,12 @@ struct sqlite3_mutex {
|
||||
#endif
|
||||
};
|
||||
#if SQLITE_MUTEX_NREF
|
||||
#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0}
|
||||
# define SQLITE3_MUTEX_INITIALIZER(id) \
|
||||
{PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0}
|
||||
#elif defined(SQLITE_ENABLE_API_ARMOR)
|
||||
#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 }
|
||||
# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id }
|
||||
#else
|
||||
#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
|
||||
#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER }
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -151,18 +152,18 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
||||
*/
|
||||
static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
static sqlite3_mutex staticMutexes[] = {
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER
|
||||
SQLITE3_MUTEX_INITIALIZER(2),
|
||||
SQLITE3_MUTEX_INITIALIZER(3),
|
||||
SQLITE3_MUTEX_INITIALIZER(4),
|
||||
SQLITE3_MUTEX_INITIALIZER(5),
|
||||
SQLITE3_MUTEX_INITIALIZER(6),
|
||||
SQLITE3_MUTEX_INITIALIZER(7),
|
||||
SQLITE3_MUTEX_INITIALIZER(8),
|
||||
SQLITE3_MUTEX_INITIALIZER(9),
|
||||
SQLITE3_MUTEX_INITIALIZER(10),
|
||||
SQLITE3_MUTEX_INITIALIZER(11),
|
||||
SQLITE3_MUTEX_INITIALIZER(12),
|
||||
SQLITE3_MUTEX_INITIALIZER(13)
|
||||
};
|
||||
sqlite3_mutex *p;
|
||||
switch( iType ){
|
||||
@@ -180,6 +181,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&p->mutex, &recursiveAttr);
|
||||
pthread_mutexattr_destroy(&recursiveAttr);
|
||||
#endif
|
||||
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
||||
p->id = SQLITE_MUTEX_RECURSIVE;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
@@ -188,6 +192,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
p = sqlite3MallocZero( sizeof(*p) );
|
||||
if( p ){
|
||||
pthread_mutex_init(&p->mutex, 0);
|
||||
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
||||
p->id = SQLITE_MUTEX_FAST;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -203,7 +210,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
}
|
||||
}
|
||||
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
||||
if( p ) p->id = iType;
|
||||
assert( p==0 || p->id==iType );
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
+17
-17
@@ -40,7 +40,7 @@ struct sqlite3_mutex {
|
||||
#ifdef SQLITE_DEBUG
|
||||
volatile int nRef; /* Number of enterances */
|
||||
volatile DWORD owner; /* Thread holding this mutex */
|
||||
volatile int trace; /* True to trace changes */
|
||||
volatile LONG trace; /* True to trace changes */
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -52,10 +52,10 @@ struct sqlite3_mutex {
|
||||
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
|
||||
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \
|
||||
0L, (DWORD)0, 0 }
|
||||
#else
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
||||
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id }
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -98,18 +98,18 @@ void sqlite3MemoryBarrier(void){
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static sqlite3_mutex winMutex_staticMutexes[] = {
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER
|
||||
SQLITE3_MUTEX_INITIALIZER(2),
|
||||
SQLITE3_MUTEX_INITIALIZER(3),
|
||||
SQLITE3_MUTEX_INITIALIZER(4),
|
||||
SQLITE3_MUTEX_INITIALIZER(5),
|
||||
SQLITE3_MUTEX_INITIALIZER(6),
|
||||
SQLITE3_MUTEX_INITIALIZER(7),
|
||||
SQLITE3_MUTEX_INITIALIZER(8),
|
||||
SQLITE3_MUTEX_INITIALIZER(9),
|
||||
SQLITE3_MUTEX_INITIALIZER(10),
|
||||
SQLITE3_MUTEX_INITIALIZER(11),
|
||||
SQLITE3_MUTEX_INITIALIZER(12),
|
||||
SQLITE3_MUTEX_INITIALIZER(13)
|
||||
};
|
||||
|
||||
static int winMutex_isInit = 0;
|
||||
@@ -239,15 +239,15 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
}
|
||||
#endif
|
||||
p = &winMutex_staticMutexes[iType-2];
|
||||
p->id = iType;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
|
||||
p->trace = 1;
|
||||
InterlockedCompareExchange(&p->trace, 1, 0);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( p==0 || p->id==iType );
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ int sqlite3OsTruncate(sqlite3_file *id, i64 size){
|
||||
}
|
||||
int sqlite3OsSync(sqlite3_file *id, int flags){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
return id->pMethods->xSync(id, flags);
|
||||
return flags ? id->pMethods->xSync(id, flags) : SQLITE_OK;
|
||||
}
|
||||
int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
@@ -125,8 +125,11 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
** routine has no return value since the return value would be meaningless.
|
||||
*/
|
||||
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
if( id->pMethods==0 ) return SQLITE_NOTFOUND;
|
||||
#ifdef SQLITE_TEST
|
||||
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
|
||||
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
|
||||
&& op!=SQLITE_FCNTL_LOCK_TIMEOUT
|
||||
){
|
||||
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
|
||||
** is using a regular VFS, it is called after the corresponding
|
||||
** transaction has been committed. Injecting a fault at this point
|
||||
@@ -143,7 +146,7 @@ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
return id->pMethods->xFileControl(id, op, pArg);
|
||||
}
|
||||
void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
|
||||
(void)id->pMethods->xFileControl(id, op, pArg);
|
||||
if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg);
|
||||
}
|
||||
|
||||
int sqlite3OsSectorSize(sqlite3_file *id){
|
||||
@@ -153,6 +156,7 @@ int sqlite3OsSectorSize(sqlite3_file *id){
|
||||
int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
|
||||
return id->pMethods->xDeviceCharacteristics(id);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
|
||||
return id->pMethods->xShmLock(id, offset, n, flags);
|
||||
}
|
||||
@@ -172,6 +176,7 @@ int sqlite3OsShmMap(
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WAL */
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/* The real implementation of xFetch and xUnfetch */
|
||||
@@ -405,9 +410,12 @@ int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
|
||||
** Unregister a VFS so that it is no longer accessible.
|
||||
*/
|
||||
int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
MUTEX_LOGIC(sqlite3_mutex *mutex;)
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(mutex);
|
||||
vfsUnlink(pVfs);
|
||||
sqlite3_mutex_leave(mutex);
|
||||
|
||||
@@ -174,10 +174,12 @@ void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
|
||||
#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
|
||||
int sqlite3OsSectorSize(sqlite3_file *id);
|
||||
int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
|
||||
int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
|
||||
void sqlite3OsShmBarrier(sqlite3_file *id);
|
||||
int sqlite3OsShmUnmap(sqlite3_file *id, int);
|
||||
#endif /* SQLITE_OMIT_WAL */
|
||||
int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
|
||||
int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
|
||||
|
||||
|
||||
+403
-163
File diff suppressed because it is too large
Load Diff
+213
-97
@@ -315,22 +315,6 @@ struct winVfsAppData {
|
||||
# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The value used with sqlite3_win32_set_directory() to specify that
|
||||
* the data directory should be changed.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
|
||||
# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The value used with sqlite3_win32_set_directory() to specify that
|
||||
* the temporary directory should be changed.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
||||
# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
|
||||
* various Win32 API heap functions instead of our own.
|
||||
@@ -1927,13 +1911,13 @@ char *sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function sets the data directory or the temporary directory based on
|
||||
** the provided arguments. The type argument must be 1 in order to set the
|
||||
** data directory or 2 in order to set the temporary directory. The zValue
|
||||
** argument is the name of the directory to use. The return value will be
|
||||
** SQLITE_OK if successful.
|
||||
** This function is the same as sqlite3_win32_set_directory (below); however,
|
||||
** it accepts a UTF-8 string.
|
||||
*/
|
||||
int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
|
||||
int sqlite3_win32_set_directory8(
|
||||
unsigned long type, /* Identifier for directory being set or reset */
|
||||
const char *zValue /* New value for directory being set or reset */
|
||||
){
|
||||
char **ppDirectory = 0;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
@@ -1949,20 +1933,53 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
|
||||
);
|
||||
assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
|
||||
if( ppDirectory ){
|
||||
char *zValueUtf8 = 0;
|
||||
char *zCopy = 0;
|
||||
if( zValue && zValue[0] ){
|
||||
zValueUtf8 = winUnicodeToUtf8(zValue);
|
||||
if ( zValueUtf8==0 ){
|
||||
zCopy = sqlite3_mprintf("%s", zValue);
|
||||
if ( zCopy==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
sqlite3_free(*ppDirectory);
|
||||
*ppDirectory = zValueUtf8;
|
||||
*ppDirectory = zCopy;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the same as sqlite3_win32_set_directory (below); however,
|
||||
** it accepts a UTF-16 string.
|
||||
*/
|
||||
int sqlite3_win32_set_directory16(
|
||||
unsigned long type, /* Identifier for directory being set or reset */
|
||||
const void *zValue /* New value for directory being set or reset */
|
||||
){
|
||||
int rc;
|
||||
char *zUtf8 = 0;
|
||||
if( zValue ){
|
||||
zUtf8 = sqlite3_win32_unicode_to_utf8(zValue);
|
||||
if( zUtf8==0 ) return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
rc = sqlite3_win32_set_directory8(type, zUtf8);
|
||||
if( zUtf8 ) sqlite3_free(zUtf8);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function sets the data directory or the temporary directory based on
|
||||
** the provided arguments. The type argument must be 1 in order to set the
|
||||
** data directory or 2 in order to set the temporary directory. The zValue
|
||||
** argument is the name of the directory to use. The return value will be
|
||||
** SQLITE_OK if successful.
|
||||
*/
|
||||
int sqlite3_win32_set_directory(
|
||||
unsigned long type, /* Identifier for directory being set or reset */
|
||||
void *zValue /* New value for directory being set or reset */
|
||||
){
|
||||
return sqlite3_win32_set_directory16(type, zValue);
|
||||
}
|
||||
|
||||
/*
|
||||
** The return value of winGetLastErrorMsg
|
||||
** is zero if the error message fits in the buffer, or non-zero
|
||||
@@ -2887,6 +2904,9 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
winFile *pFile = (winFile*)id; /* File handle object */
|
||||
int rc = SQLITE_OK; /* Return code for this function */
|
||||
DWORD lastErrno;
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
sqlite3_int64 oldMmapSize;
|
||||
#endif
|
||||
|
||||
assert( pFile );
|
||||
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
||||
@@ -2902,6 +2922,15 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
if( pFile->pMapRegion ){
|
||||
oldMmapSize = pFile->mmapSize;
|
||||
}else{
|
||||
oldMmapSize = 0;
|
||||
}
|
||||
winUnmapfile(pFile);
|
||||
#endif
|
||||
|
||||
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
|
||||
if( winSeekFile(pFile, nByte) ){
|
||||
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
|
||||
@@ -2914,12 +2943,12 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/* If the file was truncated to a size smaller than the currently
|
||||
** mapped region, reduce the effective mapping size as well. SQLite will
|
||||
** use read() and write() to access data beyond this point from now on.
|
||||
*/
|
||||
if( pFile->pMapRegion && nByte<pFile->mmapSize ){
|
||||
pFile->mmapSize = nByte;
|
||||
if( rc==SQLITE_OK && oldMmapSize>0 ){
|
||||
if( oldMmapSize>nByte ){
|
||||
winMapfile(pFile, -1);
|
||||
}else{
|
||||
winMapfile(pFile, oldMmapSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3559,6 +3588,14 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
||||
newLimit = sqlite3GlobalConfig.mxMmap;
|
||||
}
|
||||
|
||||
/* The value of newLimit may be eventually cast to (SIZE_T) and passed
|
||||
** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at
|
||||
** least a 64-bit type. */
|
||||
if( newLimit>0 && sizeof(SIZE_T)<8 ){
|
||||
newLimit = (newLimit & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
*(i64*)pArg = pFile->mmapSizeMax;
|
||||
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
||||
pFile->mmapSizeMax = newLimit;
|
||||
@@ -3623,15 +3660,16 @@ static SYSTEM_INFO winSysInfo;
|
||||
** assert( winShmMutexHeld() );
|
||||
** winShmLeaveMutex()
|
||||
*/
|
||||
static sqlite3_mutex *winBigLock = 0;
|
||||
static void winShmEnterMutex(void){
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
||||
sqlite3_mutex_enter(winBigLock);
|
||||
}
|
||||
static void winShmLeaveMutex(void){
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
||||
sqlite3_mutex_leave(winBigLock);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
static int winShmMutexHeld(void) {
|
||||
return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
||||
return sqlite3_mutex_held(winBigLock);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3665,6 +3703,9 @@ struct winShmNode {
|
||||
|
||||
int szRegion; /* Size of shared-memory regions */
|
||||
int nRegion; /* Size of array apRegion */
|
||||
u8 isReadonly; /* True if read-only */
|
||||
u8 isUnlocked; /* True if no DMS lock held */
|
||||
|
||||
struct ShmRegion {
|
||||
HANDLE hMap; /* File handle from CreateFileMapping */
|
||||
void *pMap;
|
||||
@@ -3731,7 +3772,7 @@ static int winShmSystemLock(
|
||||
int rc = 0; /* Result code form Lock/UnlockFileEx() */
|
||||
|
||||
/* Access to the winShmNode object is serialized by the caller */
|
||||
assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
|
||||
assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) );
|
||||
|
||||
OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
|
||||
pFile->hFile.h, lockType, ofst, nByte));
|
||||
@@ -3812,6 +3853,37 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
|
||||
** take it now. Return SQLITE_OK if successful, or an SQLite error
|
||||
** code otherwise.
|
||||
**
|
||||
** If the DMS cannot be locked because this is a readonly_shm=1
|
||||
** connection and no other process already holds a lock, return
|
||||
** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
|
||||
*/
|
||||
static int winLockSharedMemory(winShmNode *pShmNode){
|
||||
int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pShmNode->isReadonly ){
|
||||
pShmNode->isUnlocked = 1;
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
return SQLITE_READONLY_CANTINIT;
|
||||
}else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
||||
"winLockSharedMemory", pShmNode->zFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
}
|
||||
|
||||
return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open the shared-memory area associated with database file pDbFd.
|
||||
**
|
||||
@@ -3821,9 +3893,9 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
|
||||
*/
|
||||
static int winOpenSharedMemory(winFile *pDbFd){
|
||||
struct winShm *p; /* The connection to be opened */
|
||||
struct winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
||||
int rc; /* Result code */
|
||||
struct winShmNode *pNew; /* Newly allocated winShmNode */
|
||||
winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
winShmNode *pNew; /* Newly allocated winShmNode */
|
||||
int nName; /* Size of zName in bytes */
|
||||
|
||||
assert( pDbFd->pShm==0 ); /* Not previously opened */
|
||||
@@ -3856,6 +3928,9 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
if( pShmNode ){
|
||||
sqlite3_free(pNew);
|
||||
}else{
|
||||
int inFlags = SQLITE_OPEN_WAL;
|
||||
int outFlags = 0;
|
||||
|
||||
pShmNode = pNew;
|
||||
pNew = 0;
|
||||
((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
|
||||
@@ -3870,30 +3945,23 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
}
|
||||
}
|
||||
|
||||
rc = winOpen(pDbFd->pVfs,
|
||||
pShmNode->zFilename, /* Name of the file (UTF-8) */
|
||||
(sqlite3_file*)&pShmNode->hFile, /* File handle here */
|
||||
SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
||||
0);
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
||||
inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
}else{
|
||||
inFlags |= SQLITE_OPEN_READONLY;
|
||||
}
|
||||
rc = winOpen(pDbFd->pVfs, pShmNode->zFilename,
|
||||
(sqlite3_file*)&pShmNode->hFile,
|
||||
inFlags, &outFlags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = winLogError(rc, osGetLastError(), "winOpenShm",
|
||||
pShmNode->zFilename);
|
||||
goto shm_open_err;
|
||||
}
|
||||
if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;
|
||||
|
||||
/* Check to see if another process is holding the dead-man switch.
|
||||
** If not, truncate the file to zero length.
|
||||
*/
|
||||
if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
|
||||
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
||||
"winOpenShm", pDbFd->zPath);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
||||
}
|
||||
if( rc ) goto shm_open_err;
|
||||
rc = winLockSharedMemory(pShmNode);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
|
||||
}
|
||||
|
||||
/* Make the new connection a child of the winShmNode */
|
||||
@@ -3916,7 +3984,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
p->pNext = pShmNode->pFirst;
|
||||
pShmNode->pFirst = p;
|
||||
sqlite3_mutex_leave(pShmNode->mutex);
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
|
||||
/* Jump here on any error */
|
||||
shm_open_err:
|
||||
@@ -4120,6 +4188,8 @@ static int winShmMap(
|
||||
winFile *pDbFd = (winFile*)fd;
|
||||
winShm *pShm = pDbFd->pShm;
|
||||
winShmNode *pShmNode;
|
||||
DWORD protect = PAGE_READWRITE;
|
||||
DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( !pShm ){
|
||||
@@ -4130,6 +4200,11 @@ static int winShmMap(
|
||||
pShmNode = pShm->pShmNode;
|
||||
|
||||
sqlite3_mutex_enter(pShmNode->mutex);
|
||||
if( pShmNode->isUnlocked ){
|
||||
rc = winLockSharedMemory(pShmNode);
|
||||
if( rc!=SQLITE_OK ) goto shmpage_out;
|
||||
pShmNode->isUnlocked = 0;
|
||||
}
|
||||
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
||||
|
||||
if( pShmNode->nRegion<=iRegion ){
|
||||
@@ -4176,21 +4251,26 @@ static int winShmMap(
|
||||
}
|
||||
pShmNode->aRegion = apNew;
|
||||
|
||||
if( pShmNode->isReadonly ){
|
||||
protect = PAGE_READONLY;
|
||||
flags = FILE_MAP_READ;
|
||||
}
|
||||
|
||||
while( pShmNode->nRegion<=iRegion ){
|
||||
HANDLE hMap = NULL; /* file-mapping handle */
|
||||
void *pMap = 0; /* Mapped memory region */
|
||||
|
||||
#if SQLITE_OS_WINRT
|
||||
hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
|
||||
NULL, PAGE_READWRITE, nByte, NULL
|
||||
NULL, protect, nByte, NULL
|
||||
);
|
||||
#elif defined(SQLITE_WIN32_HAS_WIDE)
|
||||
hMap = osCreateFileMappingW(pShmNode->hFile.h,
|
||||
NULL, PAGE_READWRITE, 0, nByte, NULL
|
||||
NULL, protect, 0, nByte, NULL
|
||||
);
|
||||
#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
|
||||
hMap = osCreateFileMappingA(pShmNode->hFile.h,
|
||||
NULL, PAGE_READWRITE, 0, nByte, NULL
|
||||
NULL, protect, 0, nByte, NULL
|
||||
);
|
||||
#endif
|
||||
OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
|
||||
@@ -4200,11 +4280,11 @@ static int winShmMap(
|
||||
int iOffset = pShmNode->nRegion*szRegion;
|
||||
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
||||
#if SQLITE_OS_WINRT
|
||||
pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
||||
pMap = osMapViewOfFileFromApp(hMap, flags,
|
||||
iOffset - iOffsetShift, szRegion + iOffsetShift
|
||||
);
|
||||
#else
|
||||
pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
||||
pMap = osMapViewOfFile(hMap, flags,
|
||||
0, iOffset - iOffsetShift, szRegion + iOffsetShift
|
||||
);
|
||||
#endif
|
||||
@@ -4235,6 +4315,7 @@ shmpage_out:
|
||||
}else{
|
||||
*pp = 0;
|
||||
}
|
||||
if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
|
||||
sqlite3_mutex_leave(pShmNode->mutex);
|
||||
return rc;
|
||||
}
|
||||
@@ -4871,6 +4952,14 @@ static int winIsDir(const void *zConverted){
|
||||
return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
/* forward reference */
|
||||
static int winAccess(
|
||||
sqlite3_vfs *pVfs, /* Not used on win32 */
|
||||
const char *zFilename, /* Name of file to check */
|
||||
int flags, /* Type of test to make on this file */
|
||||
int *pResOut /* OUT: Result */
|
||||
);
|
||||
|
||||
/*
|
||||
** Open a file.
|
||||
*/
|
||||
@@ -5047,37 +5136,58 @@ static int winOpen(
|
||||
extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
|
||||
extendedParameters.lpSecurityAttributes = NULL;
|
||||
extendedParameters.hTemplateFile = NULL;
|
||||
while( (h = osCreateFile2((LPCWSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
dwCreationDisposition,
|
||||
&extendedParameters))==INVALID_HANDLE_VALUE &&
|
||||
winRetryIoerr(&cnt, &lastErrno) ){
|
||||
/* Noop */
|
||||
}
|
||||
do{
|
||||
h = osCreateFile2((LPCWSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
dwCreationDisposition,
|
||||
&extendedParameters);
|
||||
if( h!=INVALID_HANDLE_VALUE ) break;
|
||||
if( isReadWrite ){
|
||||
int rc2, isRO = 0;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rc2==SQLITE_OK && isRO ) break;
|
||||
}
|
||||
}while( winRetryIoerr(&cnt, &lastErrno) );
|
||||
#else
|
||||
while( (h = osCreateFileW((LPCWSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
winRetryIoerr(&cnt, &lastErrno) ){
|
||||
/* Noop */
|
||||
}
|
||||
do{
|
||||
h = osCreateFileW((LPCWSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL);
|
||||
if( h!=INVALID_HANDLE_VALUE ) break;
|
||||
if( isReadWrite ){
|
||||
int rc2, isRO = 0;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rc2==SQLITE_OK && isRO ) break;
|
||||
}
|
||||
}while( winRetryIoerr(&cnt, &lastErrno) );
|
||||
#endif
|
||||
}
|
||||
#ifdef SQLITE_WIN32_HAS_ANSI
|
||||
else{
|
||||
while( (h = osCreateFileA((LPCSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
winRetryIoerr(&cnt, &lastErrno) ){
|
||||
/* Noop */
|
||||
}
|
||||
do{
|
||||
h = osCreateFileA((LPCSTR)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL);
|
||||
if( h!=INVALID_HANDLE_VALUE ) break;
|
||||
if( isReadWrite ){
|
||||
int rc2, isRO = 0;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rc2==SQLITE_OK && isRO ) break;
|
||||
}
|
||||
}while( winRetryIoerr(&cnt, &lastErrno) );
|
||||
}
|
||||
#endif
|
||||
winLogIoerr(cnt, __LINE__);
|
||||
@@ -5086,8 +5196,6 @@ static int winOpen(
|
||||
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
||||
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
pFile->lastErrno = lastErrno;
|
||||
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
if( isReadWrite && !isExclusive ){
|
||||
@@ -5096,6 +5204,8 @@ static int winOpen(
|
||||
~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
|
||||
pOutFlags);
|
||||
}else{
|
||||
pFile->lastErrno = lastErrno;
|
||||
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
||||
return SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
}
|
||||
@@ -5688,9 +5798,6 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
EntropyGatherer e;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
memset(zBuf, 0, nBuf);
|
||||
#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
|
||||
rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
|
||||
#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
|
||||
e.a = (unsigned char*)zBuf;
|
||||
e.na = nBuf;
|
||||
e.nXor = 0;
|
||||
@@ -5985,6 +6092,10 @@ int sqlite3_os_init(void){
|
||||
sqlite3_vfs_register(&winLongPathNolockVfs, 0);
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
winBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -5995,6 +6106,11 @@ int sqlite3_os_end(void){
|
||||
sleepObj = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
winBigLock = 0;
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+317
-164
@@ -128,8 +128,8 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
|
||||
** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
|
||||
** struct as its argument.
|
||||
*/
|
||||
#define PAGERID(p) ((int)(p->fd))
|
||||
#define FILEHANDLEID(fd) ((int)fd)
|
||||
#define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd))
|
||||
#define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd))
|
||||
|
||||
/*
|
||||
** The Pager.eState variable stores the current 'state' of a pager. A
|
||||
@@ -616,6 +616,18 @@ struct PagerSavepoint {
|
||||
** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
|
||||
** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
|
||||
** sub-codes.
|
||||
**
|
||||
** syncFlags, walSyncFlags
|
||||
**
|
||||
** syncFlags is either SQLITE_SYNC_NORMAL (0x02) or SQLITE_SYNC_FULL (0x03).
|
||||
** syncFlags is used for rollback mode. walSyncFlags is used for WAL mode
|
||||
** and contains the flags used to sync the checkpoint operations in the
|
||||
** lower two bits, and sync flags used for transaction commits in the WAL
|
||||
** file in bits 0x04 and 0x08. In other words, to get the correct sync flags
|
||||
** for checkpoint operations, use (walSyncFlags&0x03) and to get the correct
|
||||
** sync flags for transaction commit, use ((walSyncFlags>>2)&0x03). Note
|
||||
** that with synchronous=NORMAL in WAL mode, transaction commit is not synced
|
||||
** meaning that the 0x04 and 0x08 bits are both zero.
|
||||
*/
|
||||
struct Pager {
|
||||
sqlite3_vfs *pVfs; /* OS functions to use for IO */
|
||||
@@ -625,9 +637,8 @@ struct Pager {
|
||||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 extraSync; /* sync directory after journal delete */
|
||||
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
||||
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
|
||||
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
|
||||
u8 walSyncFlags; /* See description above */
|
||||
u8 tempFile; /* zFilename is a temporary or immutable file */
|
||||
u8 noLock; /* Do not lock (except in WAL mode) */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
@@ -688,7 +699,7 @@ struct Pager {
|
||||
char *zJournal; /* Name of the journal file */
|
||||
int (*xBusyHandler)(void*); /* Function to call when busy */
|
||||
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
|
||||
int aStat[3]; /* Total cache hits, misses and writes */
|
||||
int aStat[4]; /* Total cache hits, misses, writes, spills */
|
||||
#ifdef SQLITE_TEST
|
||||
int nRead; /* Database pages read */
|
||||
#endif
|
||||
@@ -716,6 +727,7 @@ struct Pager {
|
||||
#define PAGER_STAT_HIT 0
|
||||
#define PAGER_STAT_MISS 1
|
||||
#define PAGER_STAT_WRITE 2
|
||||
#define PAGER_STAT_SPILL 3
|
||||
|
||||
/*
|
||||
** The following global variables hold counters used for
|
||||
@@ -947,6 +959,7 @@ static int assert_pager_state(Pager *p){
|
||||
assert( isOpen(p->jfd)
|
||||
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
||||
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
assert( pPager->dbOrigSize<=pPager->dbHintSize );
|
||||
break;
|
||||
@@ -958,6 +971,7 @@ static int assert_pager_state(Pager *p){
|
||||
assert( isOpen(p->jfd)
|
||||
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
||||
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -983,8 +997,12 @@ static int assert_pager_state(Pager *p){
|
||||
** to "print *pPager" in gdb:
|
||||
**
|
||||
** (gdb) printf "%s", print_pager_state(pPager)
|
||||
**
|
||||
** This routine has external linkage in order to suppress compiler warnings
|
||||
** about an unused function. It is enclosed within SQLITE_DEBUG and so does
|
||||
** not appear in normal builds.
|
||||
*/
|
||||
static char *print_pager_state(Pager *p){
|
||||
char *print_pager_state(Pager *p){
|
||||
static char zRet[1024];
|
||||
|
||||
sqlite3_snprintf(1024, zRet,
|
||||
@@ -1168,34 +1186,47 @@ static int pagerLockDb(Pager *pPager, int eLock){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function determines whether or not the atomic-write optimization
|
||||
** can be used with this pager. The optimization can be used if:
|
||||
** This function determines whether or not the atomic-write or
|
||||
** atomic-batch-write optimizations can be used with this pager. The
|
||||
** atomic-write optimization can be used if:
|
||||
**
|
||||
** (a) the value returned by OsDeviceCharacteristics() indicates that
|
||||
** a database page may be written atomically, and
|
||||
** (b) the value returned by OsSectorSize() is less than or equal
|
||||
** to the page size.
|
||||
**
|
||||
** The optimization is also always enabled for temporary files. It is
|
||||
** an error to call this function if pPager is opened on an in-memory
|
||||
** database.
|
||||
** If it can be used, then the value returned is the size of the journal
|
||||
** file when it contains rollback data for exactly one page.
|
||||
**
|
||||
** If the optimization cannot be used, 0 is returned. If it can be used,
|
||||
** then the value returned is the size of the journal file when it
|
||||
** contains rollback data for exactly one page.
|
||||
** The atomic-batch-write optimization can be used if OsDeviceCharacteristics()
|
||||
** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is
|
||||
** returned in this case.
|
||||
**
|
||||
** If neither optimization can be used, 0 is returned.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
static int jrnlBufferSize(Pager *pPager){
|
||||
assert( !MEMDB );
|
||||
if( !pPager->tempFile ){
|
||||
int dc; /* Device characteristics */
|
||||
int nSector; /* Sector size */
|
||||
int szPage; /* Page size */
|
||||
|
||||
assert( isOpen(pPager->fd) );
|
||||
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
nSector = pPager->sectorSize;
|
||||
szPage = pPager->pageSize;
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
int dc; /* Device characteristics */
|
||||
|
||||
assert( isOpen(pPager->fd) );
|
||||
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
#else
|
||||
UNUSED_PARAMETER(pPager);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
{
|
||||
int nSector = pPager->sectorSize;
|
||||
int szPage = pPager->pageSize;
|
||||
|
||||
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
||||
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
|
||||
@@ -1205,11 +1236,11 @@ static int jrnlBufferSize(Pager *pPager){
|
||||
}
|
||||
|
||||
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
|
||||
}
|
||||
#else
|
||||
# define jrnlBufferSize(x) 0
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
|
||||
** on the cache using a hash function. This is used for testing
|
||||
@@ -1291,6 +1322,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|
||||
|| szJ<16
|
||||
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|
||||
|| len>=nMaster
|
||||
|| len>szJ-16
|
||||
|| len==0
|
||||
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|
||||
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|
||||
@@ -1736,7 +1768,6 @@ static void pager_reset(Pager *pPager){
|
||||
** Return the pPager->iDataVersion value
|
||||
*/
|
||||
u32 sqlite3PagerDataVersion(Pager *pPager){
|
||||
assert( pPager->eState>PAGER_OPEN );
|
||||
return pPager->iDataVersion;
|
||||
}
|
||||
|
||||
@@ -2012,7 +2043,9 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
}
|
||||
|
||||
releaseAllSavepoints(pPager);
|
||||
assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
|
||||
assert( isOpen(pPager->jfd) || pPager->pInJournal==0
|
||||
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
if( isOpen(pPager->jfd) ){
|
||||
assert( !pagerUseWal(pPager) );
|
||||
|
||||
@@ -2100,7 +2133,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
rc = pager_truncate(pPager, pPager->dbSize);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
|
||||
if( rc==SQLITE_OK && bCommit ){
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
}
|
||||
@@ -2780,6 +2813,7 @@ static int pager_playback(Pager *pPager, int isHot){
|
||||
char *zMaster = 0; /* Name of master journal file if any */
|
||||
int needPagerReset; /* True to reset page prior to first page rollback */
|
||||
int nPlayback = 0; /* Total number of pages restored from journal */
|
||||
u32 savedPageSize = pPager->pageSize;
|
||||
|
||||
/* Figure out how many records are in the journal. Abort early if
|
||||
** the journal is empty.
|
||||
@@ -2909,15 +2943,16 @@ static int pager_playback(Pager *pPager, int isHot){
|
||||
assert( 0 );
|
||||
|
||||
end_playback:
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSetPagesize(pPager, &savedPageSize, -1);
|
||||
}
|
||||
/* Following a rollback, the database file should be back in its original
|
||||
** state prior to the start of the transaction, so invoke the
|
||||
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the
|
||||
** assertion that the transaction counter was modified.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pPager->fd->pMethods ){
|
||||
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
|
||||
}
|
||||
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
|
||||
#endif
|
||||
|
||||
/* If this playback is happening automatically as a result of an IO or
|
||||
@@ -2967,7 +3002,8 @@ end_playback:
|
||||
|
||||
|
||||
/*
|
||||
** Read the content for page pPg out of the database file and into
|
||||
** Read the content for page pPg out of the database file (or out of
|
||||
** the WAL if that is where the most recent copy if found) into
|
||||
** pPg->pData. A shared lock or greater must be held on the database
|
||||
** file before this function is called.
|
||||
**
|
||||
@@ -2977,30 +3013,33 @@ end_playback:
|
||||
** If an IO error occurs, then the IO error is returned to the caller.
|
||||
** Otherwise, SQLITE_OK is returned.
|
||||
*/
|
||||
static int readDbPage(PgHdr *pPg, u32 iFrame){
|
||||
static int readDbPage(PgHdr *pPg){
|
||||
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
|
||||
Pgno pgno = pPg->pgno; /* Page number to read */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int pgsz = pPager->pageSize; /* Number of bytes to read */
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
u32 iFrame = 0; /* Frame of WAL containing pgno */
|
||||
|
||||
assert( pPager->eState>=PAGER_READER && !MEMDB );
|
||||
assert( isOpen(pPager->fd) );
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
if( pagerUseWal(pPager) ){
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
if( iFrame ){
|
||||
/* Try to pull the page from the write-ahead log. */
|
||||
rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
|
||||
rc = sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
|
||||
rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
|
||||
i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
|
||||
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
|
||||
if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if( pgno==1 ){
|
||||
if( pPg->pgno==1 ){
|
||||
if( rc ){
|
||||
/* If the read is unsuccessful, set the dbFileVers[] to something
|
||||
** that will never be a valid file version. dbFileVers[] is a copy
|
||||
@@ -3020,13 +3059,13 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
|
||||
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
|
||||
}
|
||||
}
|
||||
CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT);
|
||||
CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT);
|
||||
|
||||
PAGER_INCR(sqlite3_pager_readdb_count);
|
||||
PAGER_INCR(pPager->nRead);
|
||||
IOTRACE(("PGIN %p %d\n", pPager, pgno));
|
||||
IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno));
|
||||
PAGERTRACE(("FETCH %d page %d hash(%08x)\n",
|
||||
PAGERID(pPager), pgno, pager_pagehash(pPg)));
|
||||
PAGERID(pPager), pPg->pgno, pager_pagehash(pPg)));
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -3077,11 +3116,7 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
|
||||
if( sqlite3PcachePageRefcount(pPg)==1 ){
|
||||
sqlite3PcacheDrop(pPg);
|
||||
}else{
|
||||
u32 iFrame = 0;
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = readDbPage(pPg, iFrame);
|
||||
}
|
||||
rc = readDbPage(pPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->xReiniter(pPg);
|
||||
}
|
||||
@@ -3587,20 +3622,17 @@ void sqlite3PagerSetFlags(
|
||||
}
|
||||
if( pPager->noSync ){
|
||||
pPager->syncFlags = 0;
|
||||
pPager->ckptSyncFlags = 0;
|
||||
}else if( pgFlags & PAGER_FULLFSYNC ){
|
||||
pPager->syncFlags = SQLITE_SYNC_FULL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
||||
}else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
||||
}else{
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
}
|
||||
pPager->walSyncFlags = pPager->syncFlags;
|
||||
pPager->walSyncFlags = (pPager->syncFlags<<2);
|
||||
if( pPager->fullSync ){
|
||||
pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
|
||||
pPager->walSyncFlags |= pPager->syncFlags;
|
||||
}
|
||||
if( (pgFlags & PAGER_CKPT_FULLFSYNC) && !pPager->noSync ){
|
||||
pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2);
|
||||
}
|
||||
if( pgFlags & PAGER_CACHESPILL ){
|
||||
pPager->doNotSpill &= ~SPILLFLAG_OFF;
|
||||
@@ -3673,20 +3705,18 @@ static int pagerOpentemp(
|
||||
** retried. If it returns zero, then the SQLITE_BUSY error is
|
||||
** returned to the caller of the pager API function.
|
||||
*/
|
||||
void sqlite3PagerSetBusyhandler(
|
||||
void sqlite3PagerSetBusyHandler(
|
||||
Pager *pPager, /* Pager object */
|
||||
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
|
||||
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
|
||||
){
|
||||
void **ap;
|
||||
pPager->xBusyHandler = xBusyHandler;
|
||||
pPager->pBusyHandlerArg = pBusyHandlerArg;
|
||||
|
||||
if( isOpen(pPager->fd) ){
|
||||
void **ap = (void **)&pPager->xBusyHandler;
|
||||
assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
|
||||
assert( ap[1]==pBusyHandlerArg );
|
||||
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
|
||||
}
|
||||
ap = (void **)&pPager->xBusyHandler;
|
||||
assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
|
||||
assert( ap[1]==pBusyHandlerArg );
|
||||
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4072,6 +4102,30 @@ static void pagerFreeMapHdrs(Pager *pPager){
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that the database file has not be deleted or renamed out from
|
||||
** under the pager. Return SQLITE_OK if the database is still where it ought
|
||||
** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
|
||||
** code from sqlite3OsAccess()) if the database has gone missing.
|
||||
*/
|
||||
static int databaseIsUnmoved(Pager *pPager){
|
||||
int bHasMoved = 0;
|
||||
int rc;
|
||||
|
||||
if( pPager->tempFile ) return SQLITE_OK;
|
||||
if( pPager->dbSize==0 ) return SQLITE_OK;
|
||||
assert( pPager->zFilename && pPager->zFilename[0] );
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
/* If the HAS_MOVED file-control is unimplemented, assume that the file
|
||||
** has not been moved. That is the historical behavior of SQLite: prior to
|
||||
** version 3.8.3, it never checked */
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK && bHasMoved ){
|
||||
rc = SQLITE_READONLY_DBMOVED;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Shutdown the page cache. Free all memory and close all files.
|
||||
@@ -4088,8 +4142,7 @@ static void pagerFreeMapHdrs(Pager *pPager){
|
||||
** to the caller.
|
||||
*/
|
||||
int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
|
||||
u8 *pTmp = (u8 *)pPager->pTmpSpace;
|
||||
|
||||
u8 *pTmp = (u8*)pPager->pTmpSpace;
|
||||
assert( db || pagerUseWal(pPager)==0 );
|
||||
assert( assert_pager_state(pPager) );
|
||||
disable_simulated_io_errors();
|
||||
@@ -4098,11 +4151,17 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
|
||||
/* pPager->errCode = 0; */
|
||||
pPager->exclusiveMode = 0;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
assert( db || pPager->pWal==0 );
|
||||
sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize,
|
||||
(db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
|
||||
);
|
||||
pPager->pWal = 0;
|
||||
{
|
||||
u8 *a = 0;
|
||||
assert( db || pPager->pWal==0 );
|
||||
if( db && 0==(db->flags & SQLITE_NoCkptOnClose)
|
||||
&& SQLITE_OK==databaseIsUnmoved(pPager)
|
||||
){
|
||||
a = pTmp;
|
||||
}
|
||||
sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
|
||||
pPager->pWal = 0;
|
||||
}
|
||||
#endif
|
||||
pager_reset(pPager);
|
||||
if( MEMDB ){
|
||||
@@ -4559,6 +4618,7 @@ static int pagerStress(void *p, PgHdr *pPg){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
pPager->aStat[PAGER_STAT_SPILL]++;
|
||||
pPg->pDirty = 0;
|
||||
if( pagerUseWal(pPager) ){
|
||||
/* Write a single frame for this page to the log. */
|
||||
@@ -4567,6 +4627,13 @@ static int pagerStress(void *p, PgHdr *pPg){
|
||||
rc = pagerWalFrames(pPager, pPg, 0, 0);
|
||||
}
|
||||
}else{
|
||||
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( pPager->tempFile==0 ){
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) return pager_error(pPager, rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sync the journal file if required. */
|
||||
if( pPg->flags&PGHDR_NEED_SYNC
|
||||
@@ -4657,6 +4724,11 @@ int sqlite3PagerOpen(
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int tempFile = 0; /* True for temp files (incl. in-memory files) */
|
||||
int memDb = 0; /* True if this is an in-memory file */
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
int memJM = 0; /* Memory journal mode */
|
||||
#else
|
||||
# define memJM 0
|
||||
#endif
|
||||
int readOnly = 0; /* True if this is a read-only file */
|
||||
int journalFileSize; /* Bytes to allocate for each journal fd */
|
||||
char *zPathname = 0; /* Full path to database file */
|
||||
@@ -4784,7 +4856,10 @@ int sqlite3PagerOpen(
|
||||
int fout = 0; /* VFS flags returned by xOpen() */
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
|
||||
assert( !memDb );
|
||||
readOnly = (fout&SQLITE_OPEN_READONLY);
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
memJM = (fout&SQLITE_OPEN_MEMORY)!=0;
|
||||
#endif
|
||||
readOnly = (fout&SQLITE_OPEN_READONLY)!=0;
|
||||
|
||||
/* If the file was successfully opened for read/write access,
|
||||
** choose a default page size in case we have to create the
|
||||
@@ -4900,13 +4975,11 @@ act_like_temp_file:
|
||||
assert( pPager->extraSync==0 );
|
||||
assert( pPager->syncFlags==0 );
|
||||
assert( pPager->walSyncFlags==0 );
|
||||
assert( pPager->ckptSyncFlags==0 );
|
||||
}else{
|
||||
pPager->fullSync = 1;
|
||||
pPager->extraSync = 0;
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
|
||||
}
|
||||
/* pPager->pFirst = 0; */
|
||||
/* pPager->pFirstSynced = 0; */
|
||||
@@ -4917,7 +4990,7 @@ act_like_temp_file:
|
||||
setSectorSize(pPager);
|
||||
if( !useJournal ){
|
||||
pPager->journalMode = PAGER_JOURNALMODE_OFF;
|
||||
}else if( memDb ){
|
||||
}else if( memDb || memJM ){
|
||||
pPager->journalMode = PAGER_JOURNALMODE_MEMORY;
|
||||
}
|
||||
/* pPager->xBusyHandler = 0; */
|
||||
@@ -4932,30 +5005,6 @@ act_like_temp_file:
|
||||
}
|
||||
|
||||
|
||||
/* Verify that the database file has not be deleted or renamed out from
|
||||
** under the pager. Return SQLITE_OK if the database is still were it ought
|
||||
** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
|
||||
** code from sqlite3OsAccess()) if the database has gone missing.
|
||||
*/
|
||||
static int databaseIsUnmoved(Pager *pPager){
|
||||
int bHasMoved = 0;
|
||||
int rc;
|
||||
|
||||
if( pPager->tempFile ) return SQLITE_OK;
|
||||
if( pPager->dbSize==0 ) return SQLITE_OK;
|
||||
assert( pPager->zFilename && pPager->zFilename[0] );
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
/* If the HAS_MOVED file-control is unimplemented, assume that the file
|
||||
** has not been moved. That is the historical behavior of SQLite: prior to
|
||||
** version 3.8.3, it never checked */
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK && bHasMoved ){
|
||||
rc = SQLITE_READONLY_DBMOVED;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called after transitioning from PAGER_UNLOCK to
|
||||
@@ -5326,7 +5375,8 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
** nothing to rollback, so this routine is a no-op.
|
||||
*/
|
||||
static void pagerUnlockIfUnused(Pager *pPager){
|
||||
if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
|
||||
if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
|
||||
assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */
|
||||
pagerUnlockAndRollback(pPager);
|
||||
}
|
||||
}
|
||||
@@ -5467,14 +5517,9 @@ static int getPageNormal(
|
||||
memset(pPg->pData, 0, pPager->pageSize);
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
u32 iFrame = 0; /* Frame to read from WAL file */
|
||||
if( pagerUseWal(pPager) ){
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
|
||||
if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
||||
}
|
||||
assert( pPg->pPager==pPager );
|
||||
pPager->aStat[PAGER_STAT_MISS]++;
|
||||
rc = readDbPage(pPg, iFrame);
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto pager_acquire_err;
|
||||
}
|
||||
@@ -5548,7 +5593,7 @@ static int getPageMMap(
|
||||
}
|
||||
if( pPg==0 ){
|
||||
rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
|
||||
}else{
|
||||
}else{
|
||||
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
|
||||
}
|
||||
if( pPg ){
|
||||
@@ -5617,25 +5662,40 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
|
||||
/*
|
||||
** Release a page reference.
|
||||
**
|
||||
** If the number of references to the page drop to zero, then the
|
||||
** page is added to the LRU list. When all references to all pages
|
||||
** are released, a rollback occurs and the lock on the database is
|
||||
** removed.
|
||||
** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be
|
||||
** used if we know that the page being released is not the last page.
|
||||
** The btree layer always holds page1 open until the end, so these first
|
||||
** to routines can be used to release any page other than BtShared.pPage1.
|
||||
**
|
||||
** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine
|
||||
** checks the total number of outstanding pages and if the number of
|
||||
** pages reaches zero it drops the database lock.
|
||||
*/
|
||||
void sqlite3PagerUnrefNotNull(DbPage *pPg){
|
||||
Pager *pPager;
|
||||
TESTONLY( Pager *pPager = pPg->pPager; )
|
||||
assert( pPg!=0 );
|
||||
pPager = pPg->pPager;
|
||||
if( pPg->flags & PGHDR_MMAP ){
|
||||
assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */
|
||||
pagerReleaseMapPage(pPg);
|
||||
}else{
|
||||
sqlite3PcacheRelease(pPg);
|
||||
}
|
||||
pagerUnlockIfUnused(pPager);
|
||||
/* Do not use this routine to release the last reference to page1 */
|
||||
assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
|
||||
}
|
||||
void sqlite3PagerUnref(DbPage *pPg){
|
||||
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
|
||||
}
|
||||
void sqlite3PagerUnrefPageOne(DbPage *pPg){
|
||||
Pager *pPager;
|
||||
assert( pPg!=0 );
|
||||
assert( pPg->pgno==1 );
|
||||
assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
|
||||
pPager = pPg->pPager;
|
||||
sqlite3PagerResetLockTimeout(pPager);
|
||||
sqlite3PcacheRelease(pPg);
|
||||
pagerUnlockIfUnused(pPager);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called at the start of every write transaction.
|
||||
@@ -6228,12 +6288,9 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
*/
|
||||
int sqlite3PagerSync(Pager *pPager, const char *zMaster){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( isOpen(pPager->fd) ){
|
||||
void *pArg = (void*)zMaster;
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
}
|
||||
void *pArg = (void*)zMaster;
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
if( rc==SQLITE_OK && !pPager->noSync ){
|
||||
assert( !MEMDB );
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
|
||||
@@ -6328,9 +6385,10 @@ int sqlite3PagerCommitPhaseOne(
|
||||
** backup in progress needs to be restarted. */
|
||||
sqlite3BackupRestart(pPager->pBackup);
|
||||
}else{
|
||||
PgHdr *pList;
|
||||
if( pagerUseWal(pPager) ){
|
||||
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
||||
PgHdr *pPageOne = 0;
|
||||
pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
||||
if( pList==0 ){
|
||||
/* Must have at least one page for the WAL commit flag.
|
||||
** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
|
||||
@@ -6347,6 +6405,21 @@ int sqlite3PagerCommitPhaseOne(
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}
|
||||
}else{
|
||||
/* The bBatch boolean is true if the batch-atomic-write commit method
|
||||
** should be used. No rollback journal is created if batch-atomic-write
|
||||
** is enabled.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
sqlite3_file *fd = pPager->fd;
|
||||
int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
|
||||
&& (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
&& !pPager->noSync
|
||||
&& sqlite3JournalIsInMemory(pPager->jfd);
|
||||
#else
|
||||
# define bBatch 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
/* The following block updates the change-counter. Exactly how it
|
||||
** does this depends on whether or not the atomic-update optimization
|
||||
** was enabled at compile time, and if this transaction meets the
|
||||
@@ -6370,33 +6443,41 @@ int sqlite3PagerCommitPhaseOne(
|
||||
** in 'direct' mode. In this case the journal file will never be
|
||||
** created for this transaction.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
PgHdr *pPg;
|
||||
assert( isOpen(pPager->jfd)
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|
||||
);
|
||||
if( !zMaster && isOpen(pPager->jfd)
|
||||
&& pPager->journalOff==jrnlBufferSize(pPager)
|
||||
&& pPager->dbSize>=pPager->dbOrigSize
|
||||
&& (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
||||
){
|
||||
/* Update the db file change counter via the direct-write method. The
|
||||
** following call will modify the in-memory representation of page 1
|
||||
** to include the updated change counter and then write page 1
|
||||
** directly to the database file. Because of the atomic-write
|
||||
** property of the host file-system, this is safe.
|
||||
*/
|
||||
rc = pager_incr_changecounter(pPager, 1);
|
||||
}else{
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
if( bBatch==0 ){
|
||||
PgHdr *pPg;
|
||||
assert( isOpen(pPager->jfd)
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|
||||
);
|
||||
if( !zMaster && isOpen(pPager->jfd)
|
||||
&& pPager->journalOff==jrnlBufferSize(pPager)
|
||||
&& pPager->dbSize>=pPager->dbOrigSize
|
||||
&& (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
||||
){
|
||||
/* Update the db file change counter via the direct-write method. The
|
||||
** following call will modify the in-memory representation of page 1
|
||||
** to include the updated change counter and then write page 1
|
||||
** directly to the database file. Because of the atomic-write
|
||||
** property of the host file-system, this is safe.
|
||||
*/
|
||||
rc = pager_incr_changecounter(pPager, 1);
|
||||
}else{
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#else /* SQLITE_ENABLE_ATOMIC_WRITE */
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( zMaster ){
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
assert( bBatch==0 );
|
||||
}
|
||||
#endif
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
#endif
|
||||
#endif /* !SQLITE_ENABLE_ATOMIC_WRITE */
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
|
||||
/* Write the master journal name into the journal file. If a master
|
||||
@@ -6419,8 +6500,37 @@ int sqlite3PagerCommitPhaseOne(
|
||||
*/
|
||||
rc = syncJournal(pPager, 0);
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
|
||||
rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
|
||||
|
||||
pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( bBatch ){
|
||||
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_write_pagelist(pPager, pList);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if( (rc&0xFF)==SQLITE_IOERR && rc!=SQLITE_IOERR_NOMEM ){
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
goto commit_phase_one_exit;
|
||||
}
|
||||
bBatch = 0;
|
||||
}else{
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
||||
|
||||
if( bBatch==0 ){
|
||||
rc = pager_write_pagelist(pPager, pList);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( rc!=SQLITE_IOERR_BLOCKED );
|
||||
goto commit_phase_one_exit;
|
||||
@@ -6641,8 +6751,12 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
|
||||
** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
|
||||
** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE,
|
||||
** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation
|
||||
** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because
|
||||
** it was added later.
|
||||
**
|
||||
** Before returning, *pnVal is incremented by the
|
||||
** current cache hit or miss count, according to the value of eStat. If the
|
||||
** reset parameter is non-zero, the cache hit or miss count is zeroed before
|
||||
** returning.
|
||||
@@ -6652,15 +6766,18 @@ void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE+1
|
||||
);
|
||||
|
||||
assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
|
||||
assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
|
||||
assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
|
||||
assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1
|
||||
&& PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 );
|
||||
|
||||
*pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
|
||||
eStat -= SQLITE_DBSTATUS_CACHE_HIT;
|
||||
*pnVal += pPager->aStat[eStat];
|
||||
if( reset ){
|
||||
pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
|
||||
pPager->aStat[eStat] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6864,6 +6981,16 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){
|
||||
return pPager->fd;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
/*
|
||||
** Reset the lock timeout for pager.
|
||||
*/
|
||||
void sqlite3PagerResetLockTimeout(Pager *pPager){
|
||||
int x = 0;
|
||||
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the file handle for the journal file (if it exists).
|
||||
** This will be either the rollback journal or the WAL file.
|
||||
@@ -7155,13 +7282,6 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
|
||||
int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
|
||||
u8 eOld = pPager->journalMode; /* Prior journalmode */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The print_pager_state() routine is intended to be used by the debugger
|
||||
** only. We invoke it once here to suppress a compiler warning. */
|
||||
print_pager_state(pPager);
|
||||
#endif
|
||||
|
||||
|
||||
/* The eMode parameter is always valid */
|
||||
assert( eMode==PAGER_JOURNALMODE_DELETE
|
||||
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|
||||
@@ -7321,9 +7441,10 @@ int sqlite3PagerCheckpoint(
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
|
||||
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
|
||||
pPager->pBusyHandlerArg,
|
||||
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
||||
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
||||
pnLog, pnCkpt
|
||||
);
|
||||
sqlite3PagerResetLockTimeout(pPager);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -7478,7 +7599,7 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
|
||||
if( rc==SQLITE_OK && pPager->pWal ){
|
||||
rc = pagerExclusiveLock(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags,
|
||||
rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags,
|
||||
pPager->pageSize, (u8*)pPager->pTmpSpace);
|
||||
pPager->pWal = 0;
|
||||
pagerFixMaplimit(pPager);
|
||||
@@ -7529,6 +7650,38 @@ int sqlite3PagerSnapshotRecover(Pager *pPager){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The caller currently has a read transaction open on the database.
|
||||
** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise,
|
||||
** this function takes a SHARED lock on the CHECKPOINTER slot and then
|
||||
** checks if the snapshot passed as the second argument is still
|
||||
** available. If so, SQLITE_OK is returned.
|
||||
**
|
||||
** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
|
||||
** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error
|
||||
** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER
|
||||
** lock is released before returning.
|
||||
*/
|
||||
int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot){
|
||||
int rc;
|
||||
if( pPager->pWal ){
|
||||
rc = sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot);
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release a lock obtained by an earlier successful call to
|
||||
** sqlite3PagerSnapshotCheck().
|
||||
*/
|
||||
void sqlite3PagerSnapshotUnlock(Pager *pPager){
|
||||
assert( pPager->pWal );
|
||||
return sqlite3WalSnapshotUnlock(pPager->pWal);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
#endif /* !SQLITE_OMIT_WAL */
|
||||
|
||||
|
||||
+9
-1
@@ -126,7 +126,7 @@ int sqlite3PagerClose(Pager *pPager, sqlite3*);
|
||||
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
|
||||
|
||||
/* Functions used to configure a Pager object. */
|
||||
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
|
||||
void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *);
|
||||
int sqlite3PagerSetPagesize(Pager*, u32*, int);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
void sqlite3PagerAlignReserve(Pager*,Pager*);
|
||||
@@ -151,6 +151,7 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
|
||||
void sqlite3PagerRef(DbPage*);
|
||||
void sqlite3PagerUnref(DbPage*);
|
||||
void sqlite3PagerUnrefNotNull(DbPage*);
|
||||
void sqlite3PagerUnrefPageOne(DbPage*);
|
||||
|
||||
/* Operations on page references. */
|
||||
int sqlite3PagerWrite(DbPage*);
|
||||
@@ -185,6 +186,8 @@ int sqlite3PagerSharedLock(Pager *pPager);
|
||||
int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
|
||||
int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
||||
int sqlite3PagerSnapshotRecover(Pager *pPager);
|
||||
int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
||||
void sqlite3PagerSnapshotUnlock(Pager *pPager);
|
||||
# endif
|
||||
#else
|
||||
# define sqlite3PagerUseWal(x,y) 0
|
||||
@@ -211,6 +214,11 @@ int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
void sqlite3PagerClearCache(Pager*);
|
||||
int sqlite3SectorSize(sqlite3_file *);
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
void sqlite3PagerResetLockTimeout(Pager *pPager);
|
||||
#else
|
||||
# define sqlite3PagerResetLockTimeout(X)
|
||||
#endif
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
void sqlite3PagerTruncateImage(Pager*,Pgno);
|
||||
|
||||
+488
-344
File diff suppressed because it is too large
Load Diff
+12
-20
@@ -191,12 +191,9 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
p->eCreate = 2;
|
||||
}
|
||||
}
|
||||
pPage->pDirtyNext = 0;
|
||||
pPage->pDirtyPrev = 0;
|
||||
}
|
||||
if( addRemove & PCACHE_DIRTYLIST_ADD ){
|
||||
assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
|
||||
|
||||
pPage->pDirtyPrev = 0;
|
||||
pPage->pDirtyNext = p->pDirty;
|
||||
if( pPage->pDirtyNext ){
|
||||
assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
||||
@@ -434,7 +431,7 @@ int sqlite3PcacheFetchStress(
|
||||
sqlite3_log(SQLITE_FULL,
|
||||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache),
|
||||
numberOfCachePages(pCache));
|
||||
#endif
|
||||
pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
|
||||
@@ -513,11 +510,7 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
||||
if( (--p->nRef)==0 ){
|
||||
if( p->flags&PGHDR_CLEAN ){
|
||||
pcacheUnpin(p);
|
||||
}else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
|
||||
** then page p is already at the head of the dirty list and the
|
||||
** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
|
||||
** tag above. */
|
||||
}else{
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
||||
}
|
||||
}
|
||||
@@ -573,16 +566,15 @@ void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
|
||||
assert( (p->flags & PGHDR_CLEAN)==0 );
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
||||
p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
||||
p->flags |= PGHDR_CLEAN;
|
||||
pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->nRef==0 ){
|
||||
pcacheUnpin(p);
|
||||
}
|
||||
assert( (p->flags & PGHDR_DIRTY)!=0 );
|
||||
assert( (p->flags & PGHDR_CLEAN)==0 );
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
||||
p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
||||
p->flags |= PGHDR_CLEAN;
|
||||
pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->nRef==0 ){
|
||||
pcacheUnpin(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ struct PgHdr {
|
||||
i16 nRef; /* Number of users of this page */
|
||||
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
|
||||
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
|
||||
/* NB: pDirtyNext and pDirtyPrev are undefined if the
|
||||
** PgHdr object is not dirty */
|
||||
};
|
||||
|
||||
/* Bit values for PgHdr.flags */
|
||||
|
||||
+32
-30
@@ -96,7 +96,6 @@ typedef struct PGroup PGroup;
|
||||
struct PgHdr1 {
|
||||
sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
|
||||
unsigned int iKey; /* Key value (page number) */
|
||||
u8 isPinned; /* Page in use, not on the LRU list */
|
||||
u8 isBulkLocal; /* This page from bulk local storage */
|
||||
u8 isAnchor; /* This is the PGroup.lru element */
|
||||
PgHdr1 *pNext; /* Next in hash table chain */
|
||||
@@ -105,6 +104,12 @@ struct PgHdr1 {
|
||||
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
|
||||
};
|
||||
|
||||
/*
|
||||
** A page is pinned if it is no on the LRU list
|
||||
*/
|
||||
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
|
||||
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
|
||||
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each other's unpinned
|
||||
** pages when they are under memory pressure. A PGroup is an instance of
|
||||
@@ -132,7 +137,7 @@ struct PGroup {
|
||||
unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
|
||||
unsigned int nMinPage; /* Sum of nMin for purgeable caches */
|
||||
unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
|
||||
unsigned int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
unsigned int nPurgeable; /* Number of purgeable pages allocated */
|
||||
PgHdr1 lru; /* The beginning and end of the LRU list */
|
||||
};
|
||||
|
||||
@@ -146,11 +151,13 @@ struct PGroup {
|
||||
*/
|
||||
struct PCache1 {
|
||||
/* Cache configuration parameters. Page size (szPage) and the purgeable
|
||||
** flag (bPurgeable) are set when the cache is created. nMax may be
|
||||
** flag (bPurgeable) and the pnPurgeable pointer are all set when the
|
||||
** cache is created and are never changed thereafter. nMax may be
|
||||
** modified at any time by a call to the pcache1Cachesize() method.
|
||||
** The PGroup mutex must be held when accessing nMax.
|
||||
*/
|
||||
PGroup *pGroup; /* PGroup this cache belongs to */
|
||||
unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */
|
||||
int szPage; /* Size of database content section */
|
||||
int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
|
||||
int szAlloc; /* Total size of one pcache line */
|
||||
@@ -245,6 +252,7 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
if( pcache1.isInit ){
|
||||
PgFreeslot *p;
|
||||
if( pBuf==0 ) sz = n = 0;
|
||||
if( n==0 ) sz = 0;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pcache1.szSlot = sz;
|
||||
pcache1.nSlot = pcache1.nFreeSlot = n;
|
||||
@@ -437,9 +445,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
|
||||
p->isBulkLocal = 0;
|
||||
p->isAnchor = 0;
|
||||
}
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
(*pCache->pnPurgeable)++;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -460,9 +466,7 @@ static void pcache1FreePage(PgHdr1 *p){
|
||||
sqlite3_free(p);
|
||||
#endif
|
||||
}
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage--;
|
||||
}
|
||||
(*pCache->pnPurgeable)--;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -557,22 +561,18 @@ static void pcache1ResizeHash(PCache1 *p){
|
||||
** The PGroup mutex must be held when this function is called.
|
||||
*/
|
||||
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
|
||||
PCache1 *pCache;
|
||||
|
||||
assert( pPage!=0 );
|
||||
assert( pPage->isPinned==0 );
|
||||
pCache = pPage->pCache;
|
||||
assert( PAGE_IS_UNPINNED(pPage) );
|
||||
assert( pPage->pLruNext );
|
||||
assert( pPage->pLruPrev );
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->isPinned = 1;
|
||||
assert( pPage->isAnchor==0 );
|
||||
assert( pCache->pGroup->lru.isAnchor==1 );
|
||||
pCache->nRecyclable--;
|
||||
assert( pPage->pCache->pGroup->lru.isAnchor==1 );
|
||||
pPage->pCache->nRecyclable--;
|
||||
return pPage;
|
||||
}
|
||||
|
||||
@@ -606,11 +606,11 @@ static void pcache1EnforceMaxPage(PCache1 *pCache){
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
PgHdr1 *p;
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
while( pGroup->nCurrentPage>pGroup->nMaxPage
|
||||
while( pGroup->nPurgeable>pGroup->nMaxPage
|
||||
&& (p=pGroup->lru.pLruPrev)->isAnchor==0
|
||||
){
|
||||
assert( p->pCache->pGroup==pGroup );
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p, 1);
|
||||
}
|
||||
@@ -659,7 +659,7 @@ static void pcache1TruncateUnsafe(
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pCache->nPage--;
|
||||
*pp = pPage->pNext;
|
||||
if( !pPage->isPinned ) pcache1PinPage(pPage);
|
||||
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
@@ -777,6 +777,10 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
|
||||
pCache->nMin = 10;
|
||||
pGroup->nMinPage += pCache->nMin;
|
||||
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
||||
pCache->pnPurgeable = &pGroup->nPurgeable;
|
||||
}else{
|
||||
static unsigned int dummyCurrentPage;
|
||||
pCache->pnPurgeable = &dummyCurrentPage;
|
||||
}
|
||||
pcache1LeaveMutex(pGroup);
|
||||
if( pCache->nHash==0 ){
|
||||
@@ -878,7 +882,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
|
||||
){
|
||||
PCache1 *pOther;
|
||||
pPage = pGroup->lru.pLruPrev;
|
||||
assert( pPage->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(pPage) );
|
||||
pcache1RemoveFromHash(pPage, 0);
|
||||
pcache1PinPage(pPage);
|
||||
pOther = pPage->pCache;
|
||||
@@ -886,7 +890,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
|
||||
pcache1FreePage(pPage);
|
||||
pPage = 0;
|
||||
}else{
|
||||
pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable);
|
||||
pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,7 +909,6 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
|
||||
pPage->pCache = pCache;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pLruNext = 0;
|
||||
pPage->isPinned = 1;
|
||||
*(void **)pPage->page.pExtra = 0;
|
||||
pCache->apHash[h] = pPage;
|
||||
if( iKey>pCache->iMaxKey ){
|
||||
@@ -991,7 +994,7 @@ static PgHdr1 *pcache1FetchNoMutex(
|
||||
** Otherwise (page not in hash and createFlag!=0) continue with
|
||||
** subsequent steps to try to create the page. */
|
||||
if( pPage ){
|
||||
if( !pPage->isPinned ){
|
||||
if( PAGE_IS_UNPINNED(pPage) ){
|
||||
return pcache1PinPage(pPage);
|
||||
}else{
|
||||
return pPage;
|
||||
@@ -1066,9 +1069,9 @@ static void pcache1Unpin(
|
||||
** part of the PGroup LRU list.
|
||||
*/
|
||||
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||
assert( pPage->isPinned==1 );
|
||||
assert( PAGE_IS_PINNED(pPage) );
|
||||
|
||||
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
|
||||
if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){
|
||||
pcache1RemoveFromHash(pPage, 1);
|
||||
}else{
|
||||
/* Add the page to the PGroup LRU list. */
|
||||
@@ -1077,7 +1080,6 @@ static void pcache1Unpin(
|
||||
(pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
|
||||
*ppFirst = pPage;
|
||||
pCache->nRecyclable++;
|
||||
pPage->isPinned = 0;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
@@ -1221,7 +1223,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
nFree += sqlite3MemSize(p);
|
||||
#endif
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p, 1);
|
||||
}
|
||||
@@ -1245,10 +1247,10 @@ void sqlite3PcacheStats(
|
||||
PgHdr1 *p;
|
||||
int nRecyclable = 0;
|
||||
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.grp.nCurrentPage;
|
||||
*pnCurrent = pcache1.grp.nPurgeable;
|
||||
*pnMax = (int)pcache1.grp.nMaxPage;
|
||||
*pnMin = (int)pcache1.grp.nMinPage;
|
||||
*pnRecyclable = nRecyclable;
|
||||
|
||||
+94
-92
@@ -298,16 +298,16 @@ static const PragmaName *pragmaLocate(const char *zName){
|
||||
/*
|
||||
** Helper subroutine for PRAGMA integrity_check:
|
||||
**
|
||||
** Generate code to output a single-column result row with the result
|
||||
** held in register regResult. Decrement the result count and halt if
|
||||
** the maximum number of result rows have been issued.
|
||||
** Generate code to output a single-column result row with a value of the
|
||||
** string held in register 3. Decrement the result count in register 1
|
||||
** and halt if the maximum number of result rows have been issued.
|
||||
*/
|
||||
static int integrityCheckResultRow(Vdbe *v, int regResult){
|
||||
static int integrityCheckResultRow(Vdbe *v){
|
||||
int addr;
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -1080,6 +1080,7 @@ void sqlite3Pragma(
|
||||
** type: Column declaration type.
|
||||
** notnull: True if 'NOT NULL' is part of column declaration
|
||||
** dflt_value: The default value for the column, if any.
|
||||
** pk: Non-zero for PK fields.
|
||||
*/
|
||||
case PragTyp_TABLE_INFO: if( zRight ){
|
||||
Table *pTab;
|
||||
@@ -1234,13 +1235,11 @@ void sqlite3Pragma(
|
||||
for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
|
||||
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash ){
|
||||
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
||||
}
|
||||
}
|
||||
for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){
|
||||
p = (FuncDef*)sqliteHashData(j);
|
||||
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1252,7 +1251,6 @@ void sqlite3Pragma(
|
||||
for(j=sqliteHashFirst(&db->aModule); j; j=sqliteHashNext(j)){
|
||||
Module *pMod = (Module*)sqliteHashData(j);
|
||||
sqlite3VdbeMultiLoad(v, 1, "s", pMod->zName);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1262,7 +1260,6 @@ void sqlite3Pragma(
|
||||
int i;
|
||||
for(i=0; i<ArraySize(aPragmaName); i++){
|
||||
sqlite3VdbeMultiLoad(v, 1, "s", aPragmaName[i].zName);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1488,12 +1485,11 @@ void sqlite3Pragma(
|
||||
|
||||
/* Do an integrity check on each database file */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
HashElem *x;
|
||||
Hash *pTbls;
|
||||
int *aRoot;
|
||||
int cnt = 0;
|
||||
int mxIdx = 0;
|
||||
int nIdx;
|
||||
HashElem *x; /* For looping over tables in the schema */
|
||||
Hash *pTbls; /* Set of all tables in the schema */
|
||||
int *aRoot; /* Array of root page numbers of all btrees */
|
||||
int cnt = 0; /* Number of entries in aRoot[] */
|
||||
int mxIdx = 0; /* Maximum number of indexes for any table */
|
||||
|
||||
if( OMIT_TEMPDB && i==1 ) continue;
|
||||
if( iDb>=0 && i!=iDb ) continue;
|
||||
@@ -1508,8 +1504,9 @@ void sqlite3Pragma(
|
||||
assert( sqlite3SchemaMutexHeld(db, i, 0) );
|
||||
pTbls = &db->aDb[i].pSchema->tblHash;
|
||||
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
Table *pTab = sqliteHashData(x); /* Current table */
|
||||
Index *pIdx; /* An index on pTab */
|
||||
int nIdx; /* Number of indexes on pTab */
|
||||
if( HasRowid(pTab) ) cnt++;
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
|
||||
if( nIdx>mxIdx ) mxIdx = nIdx;
|
||||
@@ -1519,12 +1516,12 @@ void sqlite3Pragma(
|
||||
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
if( HasRowid(pTab) ) aRoot[cnt++] = pTab->tnum;
|
||||
if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
aRoot[cnt++] = pIdx->tnum;
|
||||
aRoot[++cnt] = pIdx->tnum;
|
||||
}
|
||||
}
|
||||
aRoot[cnt] = 0;
|
||||
aRoot[0] = cnt;
|
||||
|
||||
/* Make sure sufficient number of registers have been allocated */
|
||||
pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
|
||||
@@ -1537,9 +1534,8 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
|
||||
P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
|
||||
integrityCheckResultRow(v, 2);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
/* Make sure all the indices are constructed correctly.
|
||||
@@ -1553,16 +1549,12 @@ void sqlite3Pragma(
|
||||
int r1 = -1;
|
||||
|
||||
if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
|
||||
if( pTab->pCheck==0
|
||||
&& (pTab->tabFlags & TF_HasNotNull)==0
|
||||
&& (pTab->pIndex==0 || isQuick)
|
||||
){
|
||||
continue; /* No additional checks needed for this table */
|
||||
}
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
|
||||
1, 0, &iDataCur, &iIdxCur);
|
||||
/* reg[7] counts the number of entries in the table.
|
||||
** reg[8+i] counts the number of entries in the i-th index
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
|
||||
@@ -1571,6 +1563,11 @@ void sqlite3Pragma(
|
||||
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
|
||||
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
|
||||
if( !isQuick ){
|
||||
/* Sanity check on record header decoding */
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nCol-1, 3);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
|
||||
}
|
||||
/* Verify that all NOT NULL columns really are NOT NULL */
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
char *zErr;
|
||||
@@ -1583,7 +1580,7 @@ void sqlite3Pragma(
|
||||
zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
|
||||
pTab->aCol[j].zName);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
|
||||
integrityCheckResultRow(v, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, jmp2);
|
||||
}
|
||||
/* Verify CHECK constraints */
|
||||
@@ -1595,7 +1592,6 @@ void sqlite3Pragma(
|
||||
char *zErr;
|
||||
int k;
|
||||
pParse->iSelfTab = iDataCur + 1;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
for(k=pCheck->nExpr-1; k>0; k--){
|
||||
sqlite3ExprIfFalse(pParse, pCheck->a[k].pExpr, addrCkFault, 0);
|
||||
}
|
||||
@@ -1606,57 +1602,58 @@ void sqlite3Pragma(
|
||||
zErr = sqlite3MPrintf(db, "CHECK constraint failed in %s",
|
||||
pTab->zName);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
|
||||
integrityCheckResultRow(v, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeResolveLabel(v, addrCkOk);
|
||||
sqlite3ExprCachePop(pParse);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pCheck);
|
||||
}
|
||||
/* Validate index entries for the current row */
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx && !isQuick; pIdx=pIdx->pNext, j++){
|
||||
int jmp2, jmp3, jmp4, jmp5;
|
||||
int ckUniq = sqlite3VdbeMakeLabel(v);
|
||||
if( pPk==pIdx ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
|
||||
pPrior, r1);
|
||||
pPrior = pIdx;
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */
|
||||
/* Verify that an index entry exists for the current table row */
|
||||
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
|
||||
pIdx->nColumn); VdbeCoverage(v);
|
||||
sqlite3VdbeLoadString(v, 3, "row ");
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
|
||||
sqlite3VdbeLoadString(v, 4, " missing from index ");
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
jmp4 = integrityCheckResultRow(v, 3);
|
||||
sqlite3VdbeJumpHere(v, jmp2);
|
||||
/* For UNIQUE indexes, verify that only one entry exists with the
|
||||
** current key. The entry is unique if (1) any column is NULL
|
||||
** or (2) the next entry has a different key */
|
||||
if( IsUniqueIndex(pIdx) ){
|
||||
int uniqOk = sqlite3VdbeMakeLabel(v);
|
||||
int jmp6;
|
||||
int kk;
|
||||
for(kk=0; kk<pIdx->nKeyCol; kk++){
|
||||
int iCol = pIdx->aiColumn[kk];
|
||||
assert( iCol!=XN_ROWID && iCol<pTab->nCol );
|
||||
if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
|
||||
VdbeCoverage(v);
|
||||
if( !isQuick ){ /* Omit the remaining tests for quick_check */
|
||||
/* Validate index entries for the current row */
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int jmp2, jmp3, jmp4, jmp5;
|
||||
int ckUniq = sqlite3VdbeMakeLabel(v);
|
||||
if( pPk==pIdx ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
|
||||
pPrior, r1);
|
||||
pPrior = pIdx;
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);/* increment entry count */
|
||||
/* Verify that an index entry exists for the current table row */
|
||||
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
|
||||
pIdx->nColumn); VdbeCoverage(v);
|
||||
sqlite3VdbeLoadString(v, 3, "row ");
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
|
||||
sqlite3VdbeLoadString(v, 4, " missing from index ");
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
jmp4 = integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, jmp2);
|
||||
/* For UNIQUE indexes, verify that only one entry exists with the
|
||||
** current key. The entry is unique if (1) any column is NULL
|
||||
** or (2) the next entry has a different key */
|
||||
if( IsUniqueIndex(pIdx) ){
|
||||
int uniqOk = sqlite3VdbeMakeLabel(v);
|
||||
int jmp6;
|
||||
int kk;
|
||||
for(kk=0; kk<pIdx->nKeyCol; kk++){
|
||||
int iCol = pIdx->aiColumn[kk];
|
||||
assert( iCol!=XN_ROWID && iCol<pTab->nCol );
|
||||
if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, uniqOk);
|
||||
sqlite3VdbeJumpHere(v, jmp6);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
|
||||
pIdx->nKeyCol); VdbeCoverage(v);
|
||||
sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
|
||||
sqlite3VdbeGoto(v, jmp5);
|
||||
sqlite3VdbeResolveLabel(v, uniqOk);
|
||||
}
|
||||
jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, uniqOk);
|
||||
sqlite3VdbeJumpHere(v, jmp6);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
|
||||
pIdx->nKeyCol); VdbeCoverage(v);
|
||||
sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
|
||||
sqlite3VdbeGoto(v, jmp5);
|
||||
sqlite3VdbeResolveLabel(v, uniqOk);
|
||||
sqlite3VdbeJumpHere(v, jmp4);
|
||||
sqlite3ResolvePartIdxLabel(pParse, jmp3);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, jmp4);
|
||||
sqlite3ResolvePartIdxLabel(pParse, jmp3);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, loopTop-1);
|
||||
@@ -1668,9 +1665,9 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
|
||||
sqlite3VdbeLoadString(v, 3, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
|
||||
integrityCheckResultRow(v, 7);
|
||||
sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
@@ -1684,6 +1681,9 @@ void sqlite3Pragma(
|
||||
{ OP_IfNotZero, 1, 4, 0}, /* 1 */
|
||||
{ OP_String8, 0, 3, 0}, /* 2 */
|
||||
{ OP_ResultRow, 3, 1, 0}, /* 3 */
|
||||
{ OP_Halt, 0, 0, 0}, /* 4 */
|
||||
{ OP_String8, 0, 3, 0}, /* 5 */
|
||||
{ OP_Goto, 0, 3, 0}, /* 6 */
|
||||
};
|
||||
VdbeOp *aOp;
|
||||
|
||||
@@ -1692,7 +1692,10 @@ void sqlite3Pragma(
|
||||
aOp[0].p2 = 1-mxErr;
|
||||
aOp[2].p4type = P4_STATIC;
|
||||
aOp[2].p4.z = "ok";
|
||||
aOp[5].p4type = P4_STATIC;
|
||||
aOp[5].p4.z = (char*)sqlite3ErrStr(SQLITE_CORRUPT);
|
||||
}
|
||||
sqlite3VdbeChangeP3(v, 0, sqlite3VdbeCurrentAddr(v)-2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2212,26 +2215,25 @@ static int pragmaVtabConnect(
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
sqlite3StrAccumAppendAll(&acc, "CREATE TABLE x");
|
||||
sqlite3_str_appendall(&acc, "CREATE TABLE x");
|
||||
for(i=0, j=pPragma->iPragCName; i<pPragma->nPragCName; i++, j++){
|
||||
sqlite3XPrintf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
||||
sqlite3_str_appendf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
||||
cSep = ',';
|
||||
}
|
||||
if( i==0 ){
|
||||
sqlite3XPrintf(&acc, "(\"%s\"", pPragma->zName);
|
||||
cSep = ',';
|
||||
sqlite3_str_appendf(&acc, "(\"%s\"", pPragma->zName);
|
||||
i++;
|
||||
}
|
||||
j = 0;
|
||||
if( pPragma->mPragFlg & PragFlg_Result1 ){
|
||||
sqlite3StrAccumAppendAll(&acc, ",arg HIDDEN");
|
||||
sqlite3_str_appendall(&acc, ",arg HIDDEN");
|
||||
j++;
|
||||
}
|
||||
if( pPragma->mPragFlg & (PragFlg_SchemaOpt|PragFlg_SchemaReq) ){
|
||||
sqlite3StrAccumAppendAll(&acc, ",schema HIDDEN");
|
||||
sqlite3_str_appendall(&acc, ",schema HIDDEN");
|
||||
j++;
|
||||
}
|
||||
sqlite3StrAccumAppend(&acc, ")", 1);
|
||||
sqlite3_str_append(&acc, ")", 1);
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
assert( strlen(zBuf) < sizeof(zBuf)-1 );
|
||||
rc = sqlite3_declare_vtab(db, zBuf);
|
||||
@@ -2383,13 +2385,13 @@ static int pragmaVtabFilter(
|
||||
}
|
||||
}
|
||||
sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]);
|
||||
sqlite3StrAccumAppendAll(&acc, "PRAGMA ");
|
||||
sqlite3_str_appendall(&acc, "PRAGMA ");
|
||||
if( pCsr->azArg[1] ){
|
||||
sqlite3XPrintf(&acc, "%Q.", pCsr->azArg[1]);
|
||||
sqlite3_str_appendf(&acc, "%Q.", pCsr->azArg[1]);
|
||||
}
|
||||
sqlite3StrAccumAppendAll(&acc, pTab->pName->zName);
|
||||
sqlite3_str_appendall(&acc, pTab->pName->zName);
|
||||
if( pCsr->azArg[0] ){
|
||||
sqlite3XPrintf(&acc, "=%Q", pCsr->azArg[0]);
|
||||
sqlite3_str_appendf(&acc, "=%Q", pCsr->azArg[0]);
|
||||
}
|
||||
zSql = sqlite3StrAccumFinish(&acc);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
|
||||
+75
-64
@@ -25,15 +25,23 @@ static void corruptSchema(
|
||||
const char *zExtra /* Error information */
|
||||
){
|
||||
sqlite3 *db = pData->db;
|
||||
if( !db->mallocFailed && (db->flags & SQLITE_WriteSchema)==0 ){
|
||||
if( db->mallocFailed ){
|
||||
pData->rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( pData->pzErrMsg[0]!=0 ){
|
||||
/* A error message has already been generated. Do not overwrite it */
|
||||
}else if( pData->mInitFlags & INITFLAG_AlterTable ){
|
||||
*pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
|
||||
pData->rc = SQLITE_ERROR;
|
||||
}else if( db->flags & SQLITE_WriteSchema ){
|
||||
pData->rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
char *z;
|
||||
if( zObj==0 ) zObj = "?";
|
||||
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
|
||||
if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
|
||||
sqlite3DbFree(db, *pData->pzErrMsg);
|
||||
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
|
||||
*pData->pzErrMsg = z;
|
||||
pData->rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -85,7 +93,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = saved_iDb;
|
||||
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
|
||||
/* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
@@ -132,7 +140,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** auxiliary databases. Return one of the SQLITE_ error codes to
|
||||
** indicate success or failure.
|
||||
*/
|
||||
static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
int rc;
|
||||
int i;
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
@@ -145,11 +153,14 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
const char *zMasterName;
|
||||
int openedTransaction = 0;
|
||||
|
||||
assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pSchema );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
|
||||
db->init.busy = 1;
|
||||
|
||||
/* Construct the in-memory representation schema tables (sqlite_master or
|
||||
** sqlite_temp_master) by invoking the parser directly. The appropriate
|
||||
** table name will be inserted automatically by the parser so we can just
|
||||
@@ -158,12 +169,13 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
|
||||
azArg[1] = "1";
|
||||
azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
|
||||
"rootpage integer,sql text)";
|
||||
"rootpage int,sql text)";
|
||||
azArg[3] = 0;
|
||||
initData.db = db;
|
||||
initData.iDb = iDb;
|
||||
initData.rc = SQLITE_OK;
|
||||
initData.pzErrMsg = pzErrMsg;
|
||||
initData.mInitFlags = mFlags;
|
||||
sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
||||
if( initData.rc ){
|
||||
rc = initData.rc;
|
||||
@@ -174,10 +186,10 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
*/
|
||||
pDb = &db->aDb[iDb];
|
||||
if( pDb->pBt==0 ){
|
||||
if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){
|
||||
DbSetProperty(db, 1, DB_SchemaLoaded);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
assert( iDb==1 );
|
||||
DbSetProperty(db, 1, DB_SchemaLoaded);
|
||||
rc = SQLITE_OK;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/* If there is not already a read-only (or read-write) transaction opened
|
||||
@@ -185,7 +197,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
** will be closed before this function returns. */
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0);
|
||||
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
|
||||
goto initone_error_out;
|
||||
@@ -213,6 +225,9 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
for(i=0; i<ArraySize(meta); i++){
|
||||
sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
||||
}
|
||||
if( (db->flags & SQLITE_ResetDatabase)!=0 ){
|
||||
memset(meta, 0, sizeof(meta));
|
||||
}
|
||||
pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1];
|
||||
|
||||
/* If opening a non-empty database, check the text encoding. For the
|
||||
@@ -336,9 +351,13 @@ initone_error_out:
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
|
||||
error_out:
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
sqlite3ResetOneSchema(db, iDb);
|
||||
}
|
||||
db->init.busy = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -354,42 +373,30 @@ error_out:
|
||||
*/
|
||||
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
int commit_internal = !(db->flags&SQLITE_InternChanges);
|
||||
int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
|
||||
assert( db->init.busy==0 );
|
||||
rc = SQLITE_OK;
|
||||
db->init.busy = 1;
|
||||
ENC(db) = SCHEMA_ENC(db);
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetOneSchema(db, i);
|
||||
assert( db->nDb>0 );
|
||||
/* Do the main schema first */
|
||||
if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, 0, pzErrMsg, 0);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
/* All other schemas after the main schema. The "temp" schema must be last */
|
||||
for(i=db->nDb-1; i>0; i--){
|
||||
assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
|
||||
if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg, 0);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once all the other databases have been initialized, load the schema
|
||||
** for the TEMP database. This is loaded last, as the TEMP database
|
||||
** schema may contain references to objects in other databases.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TEMPDB
|
||||
assert( db->nDb>1 );
|
||||
if( rc==SQLITE_OK && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, 1, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetOneSchema(db, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
db->init.busy = 0;
|
||||
if( rc==SQLITE_OK && commit_internal ){
|
||||
if( commit_internal ){
|
||||
sqlite3CommitInternalChanges(db);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -402,10 +409,12 @@ int sqlite3ReadSchema(Parse *pParse){
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( !db->init.busy ){
|
||||
rc = sqlite3Init(db, &pParse->zErrMsg);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
pParse->rc = rc;
|
||||
pParse->nErr++;
|
||||
if( rc!=SQLITE_OK ){
|
||||
pParse->rc = rc;
|
||||
pParse->nErr++;
|
||||
}else if( db->noSharedCache ){
|
||||
db->mDbFlags |= DBFLAG_SchemaKnownOk;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -433,7 +442,7 @@ static void schemaIsValid(Parse *pParse){
|
||||
** on the b-tree database, open one now. If a transaction is opened, it
|
||||
** will be closed immediately after reading the meta-value. */
|
||||
if( !sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
@@ -480,7 +489,8 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
*/
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( pSchema ){
|
||||
for(i=0; ALWAYS(i<db->nDb); i++){
|
||||
for(i=0; 1; i++){
|
||||
assert( i<db->nDb );
|
||||
if( db->aDb[i].pSchema==pSchema ){
|
||||
break;
|
||||
}
|
||||
@@ -494,16 +504,14 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
** Free all memory allocations in the pParse object
|
||||
*/
|
||||
void sqlite3ParserReset(Parse *pParse){
|
||||
if( pParse ){
|
||||
sqlite3 *db = pParse->db;
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
sqlite3ExprListDelete(db, pParse->pConstExpr);
|
||||
if( db ){
|
||||
assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
||||
db->lookaside.bDisable -= pParse->disableLookaside;
|
||||
}
|
||||
pParse->disableLookaside = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
sqlite3ExprListDelete(db, pParse->pConstExpr);
|
||||
if( db ){
|
||||
assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
||||
db->lookaside.bDisable -= pParse->disableLookaside;
|
||||
}
|
||||
pParse->disableLookaside = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -617,7 +625,7 @@ static int sqlite3Prepare(
|
||||
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
||||
static const char * const azColName[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
||||
"selectid", "order", "from", "detail"
|
||||
"id", "parent", "notused", "detail"
|
||||
};
|
||||
int iFirst, mx;
|
||||
if( sParse.explain==2 ){
|
||||
@@ -663,8 +671,6 @@ static int sqlite3Prepare(
|
||||
end_prepare:
|
||||
|
||||
sqlite3ParserReset(&sParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
return rc;
|
||||
}
|
||||
static int sqlite3LockAndPrepare(
|
||||
@@ -677,6 +683,7 @@ static int sqlite3LockAndPrepare(
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
int rc;
|
||||
int cnt = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
|
||||
@@ -687,14 +694,18 @@ static int sqlite3LockAndPrepare(
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
|
||||
if( rc==SQLITE_SCHEMA ){
|
||||
sqlite3_finalize(*ppStmt);
|
||||
do{
|
||||
/* Make multiple attempts to compile the SQL, until it either succeeds
|
||||
** or encounters a permanent error. A schema problem after one schema
|
||||
** reset is considered a permanent error. */
|
||||
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
|
||||
}
|
||||
assert( rc==SQLITE_OK || *ppStmt==0 );
|
||||
}while( rc==SQLITE_ERROR_RETRY
|
||||
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
assert( rc==SQLITE_OK || *ppStmt==0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
+203
-69
@@ -134,7 +134,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
** Set the StrAccum object to an error mode.
|
||||
*/
|
||||
static void setStrAccumError(StrAccum *p, u8 eError){
|
||||
assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
|
||||
assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
|
||||
p->accError = eError;
|
||||
p->nAlloc = 0;
|
||||
}
|
||||
@@ -168,8 +168,8 @@ static char *getTextArg(PrintfArguments *p){
|
||||
/*
|
||||
** Render a string given by "fmt" into the StrAccum object.
|
||||
*/
|
||||
void sqlite3VXPrintf(
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
void sqlite3_str_vappendf(
|
||||
sqlite3_str *pAccum, /* Accumulate results here */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
@@ -206,6 +206,11 @@ void sqlite3VXPrintf(
|
||||
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
|
||||
/* pAccum never starts out with an empty buffer that was obtained from
|
||||
** malloc(). This precondition is required by the mprintf("%z...")
|
||||
** optimization. */
|
||||
assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
|
||||
|
||||
bufpt = 0;
|
||||
if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){
|
||||
pArgList = va_arg(ap, PrintfArguments*);
|
||||
@@ -221,11 +226,11 @@ void sqlite3VXPrintf(
|
||||
#else
|
||||
do{ fmt++; }while( *fmt && *fmt != '%' );
|
||||
#endif
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
|
||||
sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
|
||||
if( *fmt==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
sqlite3StrAccumAppend(pAccum, "%", 1);
|
||||
sqlite3_str_append(pAccum, "%", 1);
|
||||
break;
|
||||
}
|
||||
/* Find out what flags are present */
|
||||
@@ -403,7 +408,7 @@ void sqlite3VXPrintf(
|
||||
u64 n = (u64)precision + 10 + precision/3;
|
||||
zOut = zExtra = sqlite3Malloc( n );
|
||||
if( zOut==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
nOut = (int)n;
|
||||
@@ -528,7 +533,7 @@ void sqlite3VXPrintf(
|
||||
bufpt = zExtra
|
||||
= sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
|
||||
if( bufpt==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -624,22 +629,52 @@ void sqlite3VXPrintf(
|
||||
case etCHARX:
|
||||
if( bArgList ){
|
||||
bufpt = getTextArg(pArgList);
|
||||
c = bufpt ? bufpt[0] : 0;
|
||||
length = 1;
|
||||
if( bufpt ){
|
||||
buf[0] = c = *(bufpt++);
|
||||
if( (c&0xc0)==0xc0 ){
|
||||
while( length<4 && (bufpt[0]&0xc0)==0x80 ){
|
||||
buf[length++] = *(bufpt++);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
buf[0] = 0;
|
||||
}
|
||||
}else{
|
||||
c = va_arg(ap,int);
|
||||
unsigned int ch = va_arg(ap,unsigned int);
|
||||
if( ch<0x00080 ){
|
||||
buf[0] = ch & 0xff;
|
||||
length = 1;
|
||||
}else if( ch<0x00800 ){
|
||||
buf[0] = 0xc0 + (u8)((ch>>6)&0x1f);
|
||||
buf[1] = 0x80 + (u8)(ch & 0x3f);
|
||||
length = 2;
|
||||
}else if( ch<0x10000 ){
|
||||
buf[0] = 0xe0 + (u8)((ch>>12)&0x0f);
|
||||
buf[1] = 0x80 + (u8)((ch>>6) & 0x3f);
|
||||
buf[2] = 0x80 + (u8)(ch & 0x3f);
|
||||
length = 3;
|
||||
}else{
|
||||
buf[0] = 0xf0 + (u8)((ch>>18) & 0x07);
|
||||
buf[1] = 0x80 + (u8)((ch>>12) & 0x3f);
|
||||
buf[2] = 0x80 + (u8)((ch>>6) & 0x3f);
|
||||
buf[3] = 0x80 + (u8)(ch & 0x3f);
|
||||
length = 4;
|
||||
}
|
||||
}
|
||||
if( precision>1 ){
|
||||
width -= precision-1;
|
||||
if( width>1 && !flag_leftjustify ){
|
||||
sqlite3AppendChar(pAccum, width-1, ' ');
|
||||
sqlite3_str_appendchar(pAccum, width-1, ' ');
|
||||
width = 0;
|
||||
}
|
||||
sqlite3AppendChar(pAccum, precision-1, c);
|
||||
while( precision-- > 1 ){
|
||||
sqlite3_str_append(pAccum, buf, length);
|
||||
}
|
||||
}
|
||||
length = 1;
|
||||
buf[0] = c;
|
||||
bufpt = buf;
|
||||
break;
|
||||
flag_altform2 = 1;
|
||||
goto adjust_width_for_utf8;
|
||||
case etSTRING:
|
||||
case etDYNSTRING:
|
||||
if( bArgList ){
|
||||
@@ -651,17 +686,50 @@ void sqlite3VXPrintf(
|
||||
if( bufpt==0 ){
|
||||
bufpt = "";
|
||||
}else if( xtype==etDYNSTRING ){
|
||||
if( pAccum->nChar==0
|
||||
&& pAccum->mxAlloc
|
||||
&& width==0
|
||||
&& precision<0
|
||||
&& pAccum->accError==0
|
||||
){
|
||||
/* Special optimization for sqlite3_mprintf("%z..."):
|
||||
** Extend an existing memory allocation rather than creating
|
||||
** a new one. */
|
||||
assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
|
||||
pAccum->zText = bufpt;
|
||||
pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt);
|
||||
pAccum->nChar = 0x7fffffff & (int)strlen(bufpt);
|
||||
pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
||||
length = 0;
|
||||
break;
|
||||
}
|
||||
zExtra = bufpt;
|
||||
}
|
||||
if( precision>=0 ){
|
||||
for(length=0; length<precision && bufpt[length]; length++){}
|
||||
if( flag_altform2 ){
|
||||
/* Set length to the number of bytes needed in order to display
|
||||
** precision characters */
|
||||
unsigned char *z = (unsigned char*)bufpt;
|
||||
while( precision-- > 0 && z[0] ){
|
||||
SQLITE_SKIP_UTF8(z);
|
||||
}
|
||||
length = (int)(z - (unsigned char*)bufpt);
|
||||
}else{
|
||||
for(length=0; length<precision && bufpt[length]; length++){}
|
||||
}
|
||||
}else{
|
||||
length = sqlite3Strlen30(bufpt);
|
||||
length = 0x7fffffff & (int)strlen(bufpt);
|
||||
}
|
||||
adjust_width_for_utf8:
|
||||
if( flag_altform2 && width>0 ){
|
||||
/* Adjust width to account for extra bytes in UTF-8 characters */
|
||||
int ii = length - 1;
|
||||
while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
|
||||
}
|
||||
break;
|
||||
case etSQLESCAPE: /* Escape ' characters */
|
||||
case etSQLESCAPE2: /* Escape ' and enclose in '...' */
|
||||
case etSQLESCAPE3: { /* Escape " characters */
|
||||
case etSQLESCAPE: /* %q: Escape ' characters */
|
||||
case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */
|
||||
case etSQLESCAPE3: { /* %w: Escape " characters */
|
||||
int i, j, k, n, isnull;
|
||||
int needQuote;
|
||||
char ch;
|
||||
@@ -675,16 +743,24 @@ void sqlite3VXPrintf(
|
||||
}
|
||||
isnull = escarg==0;
|
||||
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
||||
/* For %q, %Q, and %w, the precision is the number of byte (or
|
||||
** characters if the ! flags is present) to use from the input.
|
||||
** Because of the extra quoting characters inserted, the number
|
||||
** of output characters may be larger than the precision.
|
||||
*/
|
||||
k = precision;
|
||||
for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
|
||||
if( ch==q ) n++;
|
||||
if( flag_altform2 && (ch&0xc0)==0xc0 ){
|
||||
while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
|
||||
}
|
||||
}
|
||||
needQuote = !isnull && xtype==etSQLESCAPE2;
|
||||
n += i + 3;
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqlite3Malloc( n );
|
||||
if( bufpt==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
@@ -700,10 +776,7 @@ void sqlite3VXPrintf(
|
||||
if( needQuote ) bufpt[j++] = q;
|
||||
bufpt[j] = 0;
|
||||
length = j;
|
||||
/* The precision in %q and %Q means how many input characters to
|
||||
** consume, not the length of the output...
|
||||
** if( precision>=0 && precision<length ) length = precision; */
|
||||
break;
|
||||
goto adjust_width_for_utf8;
|
||||
}
|
||||
case etTOKEN: {
|
||||
Token *pToken;
|
||||
@@ -711,7 +784,7 @@ void sqlite3VXPrintf(
|
||||
pToken = va_arg(ap, Token*);
|
||||
assert( bArgList==0 );
|
||||
if( pToken && pToken->n ){
|
||||
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
||||
sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
|
||||
}
|
||||
length = width = 0;
|
||||
break;
|
||||
@@ -727,10 +800,10 @@ void sqlite3VXPrintf(
|
||||
assert( bArgList==0 );
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
|
||||
sqlite3StrAccumAppend(pAccum, ".", 1);
|
||||
sqlite3_str_appendall(pAccum, pItem->zDatabase);
|
||||
sqlite3_str_append(pAccum, ".", 1);
|
||||
}
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zName);
|
||||
sqlite3_str_appendall(pAccum, pItem->zName);
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
@@ -742,15 +815,18 @@ void sqlite3VXPrintf(
|
||||
/*
|
||||
** The text of the conversion is pointed to by "bufpt" and is
|
||||
** "length" characters long. The field width is "width". Do
|
||||
** the output.
|
||||
** the output. Both length and width are in bytes, not characters,
|
||||
** at this point. If the "!" flag was present on string conversions
|
||||
** indicating that width and precision should be expressed in characters,
|
||||
** then the values have been translated prior to reaching this point.
|
||||
*/
|
||||
width -= length;
|
||||
if( width>0 ){
|
||||
if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
if( !flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
||||
sqlite3_str_append(pAccum, bufpt, length);
|
||||
if( flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
||||
}else{
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
sqlite3_str_append(pAccum, bufpt, length);
|
||||
}
|
||||
|
||||
if( zExtra ){
|
||||
@@ -771,18 +847,17 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
char *zNew;
|
||||
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
|
||||
if( p->accError ){
|
||||
testcase(p->accError==STRACCUM_TOOBIG);
|
||||
testcase(p->accError==STRACCUM_NOMEM);
|
||||
testcase(p->accError==SQLITE_TOOBIG);
|
||||
testcase(p->accError==SQLITE_NOMEM);
|
||||
return 0;
|
||||
}
|
||||
if( p->mxAlloc==0 ){
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
setStrAccumError(p, SQLITE_TOOBIG);
|
||||
return N;
|
||||
}else{
|
||||
char *zOld = isMalloced(p) ? p->zText : 0;
|
||||
i64 szNew = p->nChar;
|
||||
assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
||||
szNew += N + 1;
|
||||
if( szNew+p->nChar<=p->mxAlloc ){
|
||||
/* Force exponential buffer size growth as long as it does not overflow,
|
||||
@@ -790,8 +865,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
szNew += p->nChar;
|
||||
}
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
sqlite3_str_reset(p);
|
||||
setStrAccumError(p, SQLITE_TOOBIG);
|
||||
return 0;
|
||||
}else{
|
||||
p->nAlloc = (int)szNew;
|
||||
@@ -808,8 +883,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
|
||||
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
||||
}else{
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
sqlite3_str_reset(p);
|
||||
setStrAccumError(p, SQLITE_NOMEM);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -819,12 +894,11 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
/*
|
||||
** Append N copies of character c to the given string buffer.
|
||||
*/
|
||||
void sqlite3AppendChar(StrAccum *p, int N, char c){
|
||||
void sqlite3_str_appendchar(sqlite3_str *p, int N, char c){
|
||||
testcase( p->nChar + (i64)N > 0x7fffffff );
|
||||
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
||||
return;
|
||||
}
|
||||
assert( (p->zText==p->zBase)==!isMalloced(p) );
|
||||
while( (N--)>0 ) p->zText[p->nChar++] = c;
|
||||
}
|
||||
|
||||
@@ -832,9 +906,9 @@ void sqlite3AppendChar(StrAccum *p, int N, char c){
|
||||
** The StrAccum "p" is not large enough to accept N new bytes of z[].
|
||||
** So enlarge if first, then do the append.
|
||||
**
|
||||
** This is a helper routine to sqlite3StrAccumAppend() that does special-case
|
||||
** This is a helper routine to sqlite3_str_append() that does special-case
|
||||
** work (enlarging the buffer) using tail recursion, so that the
|
||||
** sqlite3StrAccumAppend() routine can use fast calling semantics.
|
||||
** sqlite3_str_append() routine can use fast calling semantics.
|
||||
*/
|
||||
static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
||||
N = sqlite3StrAccumEnlarge(p, N);
|
||||
@@ -842,14 +916,13 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
||||
memcpy(&p->zText[p->nChar], z, N);
|
||||
p->nChar += N;
|
||||
}
|
||||
assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Append N bytes of text from z to the StrAccum object. Increase the
|
||||
** size of the memory allocation for StrAccum if necessary.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
|
||||
assert( z!=0 || N==0 );
|
||||
assert( p->zText!=0 || p->nChar==0 || p->accError );
|
||||
assert( N>=0 );
|
||||
@@ -866,8 +939,8 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
/*
|
||||
** Append the complete text of zero-terminated string z[] to the p string.
|
||||
*/
|
||||
void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
||||
sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
|
||||
void sqlite3_str_appendall(sqlite3_str *p, const char *z){
|
||||
sqlite3_str_append(p, z, sqlite3Strlen30(z));
|
||||
}
|
||||
|
||||
|
||||
@@ -877,19 +950,20 @@ void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
||||
** pointer if any kind of error was encountered.
|
||||
*/
|
||||
static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
|
||||
char *zText;
|
||||
assert( p->mxAlloc>0 && !isMalloced(p) );
|
||||
p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
||||
if( p->zText ){
|
||||
memcpy(p->zText, p->zBase, p->nChar+1);
|
||||
zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
||||
if( zText ){
|
||||
memcpy(zText, p->zText, p->nChar+1);
|
||||
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
||||
}else{
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
setStrAccumError(p, SQLITE_NOMEM);
|
||||
}
|
||||
return p->zText;
|
||||
p->zText = zText;
|
||||
return zText;
|
||||
}
|
||||
char *sqlite3StrAccumFinish(StrAccum *p){
|
||||
if( p->zText ){
|
||||
assert( (p->zText==p->zBase)==!isMalloced(p) );
|
||||
p->zText[p->nChar] = 0;
|
||||
if( p->mxAlloc>0 && !isMalloced(p) ){
|
||||
return strAccumFinishRealloc(p);
|
||||
@@ -898,15 +972,56 @@ char *sqlite3StrAccumFinish(StrAccum *p){
|
||||
return p->zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** This singleton is an sqlite3_str object that is returned if
|
||||
** sqlite3_malloc() fails to provide space for a real one. This
|
||||
** sqlite3_str object accepts no new text and always returns
|
||||
** an SQLITE_NOMEM error.
|
||||
*/
|
||||
static sqlite3_str sqlite3OomStr = {
|
||||
0, 0, 0, 0, 0, SQLITE_NOMEM, 0
|
||||
};
|
||||
|
||||
/* Finalize a string created using sqlite3_str_new().
|
||||
*/
|
||||
char *sqlite3_str_finish(sqlite3_str *p){
|
||||
char *z;
|
||||
if( p!=0 && p!=&sqlite3OomStr ){
|
||||
z = sqlite3StrAccumFinish(p);
|
||||
sqlite3_free(p);
|
||||
}else{
|
||||
z = 0;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/* Return any error code associated with p */
|
||||
int sqlite3_str_errcode(sqlite3_str *p){
|
||||
return p ? p->accError : SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Return the current length of p in bytes */
|
||||
int sqlite3_str_length(sqlite3_str *p){
|
||||
return p ? p->nChar : 0;
|
||||
}
|
||||
|
||||
/* Return the current value for p */
|
||||
char *sqlite3_str_value(sqlite3_str *p){
|
||||
if( p==0 || p->nChar==0 ) return 0;
|
||||
p->zText[p->nChar] = 0;
|
||||
return p->zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset an StrAccum string. Reclaim all malloced memory.
|
||||
*/
|
||||
void sqlite3StrAccumReset(StrAccum *p){
|
||||
assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
||||
void sqlite3_str_reset(StrAccum *p){
|
||||
if( isMalloced(p) ){
|
||||
sqlite3DbFree(p->db, p->zText);
|
||||
p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
|
||||
}
|
||||
p->nAlloc = 0;
|
||||
p->nChar = 0;
|
||||
p->zText = 0;
|
||||
}
|
||||
|
||||
@@ -925,15 +1040,27 @@ void sqlite3StrAccumReset(StrAccum *p){
|
||||
** allocations will ever occur.
|
||||
*/
|
||||
void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
|
||||
p->zText = p->zBase = zBase;
|
||||
p->zText = zBase;
|
||||
p->db = db;
|
||||
p->nChar = 0;
|
||||
p->nAlloc = n;
|
||||
p->mxAlloc = mx;
|
||||
p->nChar = 0;
|
||||
p->accError = 0;
|
||||
p->printfFlags = 0;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a new dynamic string object */
|
||||
sqlite3_str *sqlite3_str_new(sqlite3 *db){
|
||||
sqlite3_str *p = sqlite3_malloc64(sizeof(*p));
|
||||
if( p ){
|
||||
sqlite3StrAccumInit(p, 0, 0, 0,
|
||||
db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
|
||||
}else{
|
||||
p = &sqlite3OomStr;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
@@ -946,9 +1073,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
||||
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
acc.printfFlags = SQLITE_PRINTF_INTERNAL;
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
if( acc.accError==STRACCUM_NOMEM ){
|
||||
if( acc.accError==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
return z;
|
||||
@@ -986,7 +1113,7 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
return z;
|
||||
}
|
||||
@@ -1031,7 +1158,7 @@ char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
||||
}
|
||||
#endif
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
zBuf[acc.nChar] = 0;
|
||||
return zBuf;
|
||||
}
|
||||
@@ -1053,7 +1180,7 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
** allocate memory because it might be called while the memory allocator
|
||||
** mutex is held.
|
||||
**
|
||||
** sqlite3VXPrintf() might ask for *temporary* memory allocations for
|
||||
** sqlite3_str_vappendf() might ask for *temporary* memory allocations for
|
||||
** certain format characters (%q) or for very large precisions or widths.
|
||||
** Care must be taken that any sqlite3_log() calls that occur while the
|
||||
** memory mutex is held do not use these mechanisms.
|
||||
@@ -1063,7 +1190,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
|
||||
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
|
||||
|
||||
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
|
||||
sqlite3StrAccumFinish(&acc));
|
||||
}
|
||||
@@ -1092,22 +1219,29 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
char zBuf[500];
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
va_end(ap);
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
#ifdef SQLITE_OS_TRACE_PROC
|
||||
{
|
||||
extern void SQLITE_OS_TRACE_PROC(const char *zBuf, int nBuf);
|
||||
SQLITE_OS_TRACE_PROC(zBuf, sizeof(zBuf));
|
||||
}
|
||||
#else
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
|
||||
** variable-argument wrapper around sqlite3_str_vappendf(). The bFlags argument
|
||||
** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
|
||||
*/
|
||||
void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
||||
void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(p, zFormat, ap);
|
||||
sqlite3_str_vappendf(p, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
+243
-105
@@ -75,29 +75,31 @@ static void resolveAlias(
|
||||
assert( pOrig!=0 );
|
||||
db = pParse->db;
|
||||
pDup = sqlite3ExprDup(db, pOrig, 0);
|
||||
if( pDup==0 ) return;
|
||||
if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
|
||||
if( pExpr->op==TK_COLLATE ){
|
||||
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
||||
}
|
||||
ExprSetProperty(pDup, EP_Alias);
|
||||
if( pDup!=0 ){
|
||||
if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
|
||||
if( pExpr->op==TK_COLLATE ){
|
||||
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
||||
}
|
||||
ExprSetProperty(pDup, EP_Alias);
|
||||
|
||||
/* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
|
||||
** prevents ExprDelete() from deleting the Expr structure itself,
|
||||
** allowing it to be repopulated by the memcpy() on the following line.
|
||||
** The pExpr->u.zToken might point into memory that will be freed by the
|
||||
** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
|
||||
** make a copy of the token before doing the sqlite3DbFree().
|
||||
*/
|
||||
ExprSetProperty(pExpr, EP_Static);
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
memcpy(pExpr, pDup, sizeof(*pExpr));
|
||||
if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
|
||||
assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
|
||||
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
||||
pExpr->flags |= EP_MemToken;
|
||||
/* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
|
||||
** prevents ExprDelete() from deleting the Expr structure itself,
|
||||
** allowing it to be repopulated by the memcpy() on the following line.
|
||||
** The pExpr->u.zToken might point into memory that will be freed by the
|
||||
** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
|
||||
** make a copy of the token before doing the sqlite3DbFree().
|
||||
*/
|
||||
ExprSetProperty(pExpr, EP_Static);
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
memcpy(pExpr, pDup, sizeof(*pExpr));
|
||||
if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
|
||||
assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
|
||||
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
||||
pExpr->flags |= EP_MemToken;
|
||||
}
|
||||
sqlite3DbFree(db, pDup);
|
||||
}
|
||||
sqlite3DbFree(db, pDup);
|
||||
ExprSetProperty(pExpr, EP_Alias);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +193,7 @@ static int lookupName(
|
||||
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
|
||||
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
||||
Schema *pSchema = 0; /* Schema of the expression */
|
||||
int isTrigger = 0; /* True if resolved to a trigger column */
|
||||
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
|
||||
Table *pTab = 0; /* Table hold the row */
|
||||
Column *pCol; /* A column of pTab */
|
||||
|
||||
@@ -262,6 +264,9 @@ static int lookupName(
|
||||
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
|
||||
continue;
|
||||
}
|
||||
if( IN_RENAME_OBJECT && pItem->zAlias ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab);
|
||||
}
|
||||
}
|
||||
if( 0==(cntTab++) ){
|
||||
pMatch = pItem;
|
||||
@@ -296,22 +301,35 @@ static int lookupName(
|
||||
}
|
||||
} /* if( pSrcList ) */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
|
||||
/* If we have not already resolved the name, then maybe
|
||||
** it is a new.* or old.* trigger argument reference
|
||||
** it is a new.* or old.* trigger argument reference. Or
|
||||
** maybe it is an excluded.* from an upsert.
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
|
||||
int op = pParse->eTriggerOp;
|
||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
pExpr->iTable = 1;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
||||
pExpr->iTable = 0;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else{
|
||||
pTab = 0;
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 ){
|
||||
pTab = 0;
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( pParse->pTriggerTab!=0 ){
|
||||
int op = pParse->eTriggerOp;
|
||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
pExpr->iTable = 1;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
||||
pExpr->iTable = 0;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( (pNC->ncFlags & NC_UUpsert)!=0 ){
|
||||
Upsert *pUpsert = pNC->uNC.pUpsert;
|
||||
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
||||
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
||||
pExpr->iTable = 2;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_UPSERT */
|
||||
|
||||
if( pTab ){
|
||||
int iCol;
|
||||
@@ -331,24 +349,42 @@ static int lookupName(
|
||||
}
|
||||
if( iCol<pTab->nCol ){
|
||||
cnt++;
|
||||
if( iCol<0 ){
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
}else if( pExpr->iTable==0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}else{
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pExpr->iTable==2 ){
|
||||
testcase( iCol==(-1) );
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pExpr->iColumn = iCol;
|
||||
pExpr->pTab = pTab;
|
||||
eNewExprOp = TK_COLUMN;
|
||||
}else{
|
||||
pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
|
||||
eNewExprOp = TK_REGISTER;
|
||||
ExprSetProperty(pExpr, EP_Alias);
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_UPSERT */
|
||||
{
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( iCol<0 ){
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
}else if( pExpr->iTable==0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}else{
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}
|
||||
pExpr->pTab = pTab;
|
||||
pExpr->iColumn = (i16)iCol;
|
||||
eNewExprOp = TK_TRIGGER;
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
}
|
||||
pExpr->iColumn = (i16)iCol;
|
||||
pExpr->pTab = pTab;
|
||||
isTrigger = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
|
||||
#endif /* !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) */
|
||||
|
||||
/*
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
@@ -383,10 +419,12 @@ static int lookupName(
|
||||
** is supported for backwards compatibility only. Hence, we issue a warning
|
||||
** on sqlite3_log() whenever the capability is used.
|
||||
*/
|
||||
if( (pEList = pNC->pEList)!=0
|
||||
&& zTab==0
|
||||
if( (pNC->ncFlags & NC_UEList)!=0
|
||||
&& cnt==0
|
||||
&& zTab==0
|
||||
){
|
||||
pEList = pNC->uNC.pEList;
|
||||
assert( pEList!=0 );
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
char *zAs = pEList->a[j].zName;
|
||||
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
|
||||
@@ -407,6 +445,9 @@ static int lookupName(
|
||||
cnt = 1;
|
||||
pMatch = 0;
|
||||
assert( zTab==0 && zDb==0 );
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
|
||||
}
|
||||
goto lookupname_end;
|
||||
}
|
||||
}
|
||||
@@ -431,10 +472,16 @@ static int lookupName(
|
||||
** Because no reference was made to outer contexts, the pNC->nRef
|
||||
** fields are not changed in any context.
|
||||
*/
|
||||
if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){
|
||||
pExpr->op = TK_STRING;
|
||||
pExpr->pTab = 0;
|
||||
return WRC_Prune;
|
||||
if( cnt==0 && zTab==0 ){
|
||||
assert( pExpr->op==TK_ID );
|
||||
if( ExprHasProperty(pExpr,EP_DblQuoted) ){
|
||||
pExpr->op = TK_STRING;
|
||||
pExpr->pTab = 0;
|
||||
return WRC_Prune;
|
||||
}
|
||||
if( sqlite3ExprIdToTrueFalse(pExpr) ){
|
||||
return WRC_Prune;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -477,7 +524,7 @@ static int lookupName(
|
||||
pExpr->pLeft = 0;
|
||||
sqlite3ExprDelete(db, pExpr->pRight);
|
||||
pExpr->pRight = 0;
|
||||
pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN);
|
||||
pExpr->op = eNewExprOp;
|
||||
ExprSetProperty(pExpr, EP_Leaf);
|
||||
lookupname_end:
|
||||
if( cnt==1 ){
|
||||
@@ -596,7 +643,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
SrcList *pSrcList = pNC->pSrcList;
|
||||
struct SrcList_item *pItem;
|
||||
assert( pSrcList && pSrcList->nSrc==1 );
|
||||
pItem = pSrcList->a;
|
||||
pItem = pSrcList->a;
|
||||
assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
|
||||
pExpr->op = TK_COLUMN;
|
||||
pExpr->pTab = pItem->pTab;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
@@ -627,17 +675,24 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
zTable = 0;
|
||||
zColumn = pExpr->u.zToken;
|
||||
}else{
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
|
||||
pRight = pExpr->pRight;
|
||||
if( pRight->op==TK_ID ){
|
||||
zDb = 0;
|
||||
zTable = pExpr->pLeft->u.zToken;
|
||||
zColumn = pRight->u.zToken;
|
||||
}else{
|
||||
assert( pRight->op==TK_DOT );
|
||||
zDb = pExpr->pLeft->u.zToken;
|
||||
zTable = pRight->pLeft->u.zToken;
|
||||
zColumn = pRight->pRight->u.zToken;
|
||||
zDb = pLeft->u.zToken;
|
||||
pLeft = pRight->pLeft;
|
||||
pRight = pRight->pRight;
|
||||
}
|
||||
zTable = pLeft->u.zToken;
|
||||
zColumn = pRight->u.zToken;
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
|
||||
}
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft);
|
||||
}
|
||||
}
|
||||
return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
|
||||
@@ -720,40 +775,95 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
NC_IdxExpr|NC_PartIdx);
|
||||
}
|
||||
}
|
||||
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
|
||||
pNC->nErr++;
|
||||
is_agg = 0;
|
||||
}else if( no_such_func && pParse->db->init.busy==0
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
&& pParse->explain==0
|
||||
|
||||
if( 0==IN_RENAME_OBJECT ){
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|
||||
|| (pDef->xValue==0 && pDef->xInverse==0)
|
||||
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
|
||||
);
|
||||
if( pDef && pDef->xValue==0 && pExpr->pWin ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"%.*s() may not be used as a window function", nId, zId
|
||||
);
|
||||
pNC->nErr++;
|
||||
}else if(
|
||||
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
|
||||
|| (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin)
|
||||
|| (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0)
|
||||
){
|
||||
const char *zType;
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){
|
||||
zType = "window";
|
||||
}else{
|
||||
zType = "aggregate";
|
||||
}
|
||||
sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
|
||||
pNC->nErr++;
|
||||
is_agg = 0;
|
||||
}
|
||||
#else
|
||||
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
|
||||
sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId);
|
||||
pNC->nErr++;
|
||||
is_agg = 0;
|
||||
}
|
||||
#endif
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
||||
pNC->nErr++;
|
||||
}else if( wrong_num_args ){
|
||||
sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
|
||||
nId, zId);
|
||||
pNC->nErr++;
|
||||
else if( no_such_func && pParse->db->init.busy==0
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
&& pParse->explain==0
|
||||
#endif
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
||||
pNC->nErr++;
|
||||
}else if( wrong_num_args ){
|
||||
sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
|
||||
nId, zId);
|
||||
pNC->nErr++;
|
||||
}
|
||||
if( is_agg ){
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg);
|
||||
#else
|
||||
pNC->ncFlags &= ~NC_AllowAgg;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
|
||||
sqlite3WalkExprList(pWalker, pList);
|
||||
if( is_agg ){
|
||||
NameContext *pNC2 = pNC;
|
||||
pExpr->op = TK_AGG_FUNCTION;
|
||||
pExpr->op2 = 0;
|
||||
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
|
||||
pExpr->op2++;
|
||||
pNC2 = pNC2->pNext;
|
||||
}
|
||||
assert( pDef!=0 );
|
||||
if( pNC2 ){
|
||||
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
||||
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
||||
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pExpr->pWin ){
|
||||
Select *pSel = pNC->pWinSelect;
|
||||
sqlite3WalkExprList(pWalker, pExpr->pWin->pPartition);
|
||||
sqlite3WalkExprList(pWalker, pExpr->pWin->pOrderBy);
|
||||
sqlite3WalkExpr(pWalker, pExpr->pWin->pFilter);
|
||||
sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->pWin, pDef);
|
||||
if( 0==pSel->pWin
|
||||
|| 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->pWin)
|
||||
){
|
||||
pExpr->pWin->pNextWin = pSel->pWin;
|
||||
pSel->pWin = pExpr->pWin;
|
||||
}
|
||||
pNC->ncFlags |= NC_AllowWin;
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
{
|
||||
NameContext *pNC2 = pNC;
|
||||
pExpr->op = TK_AGG_FUNCTION;
|
||||
pExpr->op2 = 0;
|
||||
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
|
||||
pExpr->op2++;
|
||||
pNC2 = pNC2->pNext;
|
||||
}
|
||||
assert( pDef!=0 );
|
||||
if( pNC2 ){
|
||||
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
||||
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
||||
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
|
||||
|
||||
}
|
||||
pNC->ncFlags |= NC_AllowAgg;
|
||||
}
|
||||
pNC->ncFlags |= NC_AllowAgg;
|
||||
}
|
||||
/* FIX ME: Compute pExpr->affinity based on the expected return
|
||||
** type of the function
|
||||
@@ -782,15 +892,30 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
||||
break;
|
||||
}
|
||||
case TK_IS:
|
||||
case TK_ISNOT: {
|
||||
Expr *pRight;
|
||||
assert( !ExprHasProperty(pExpr, EP_Reduced) );
|
||||
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
|
||||
** and "x IS NOT FALSE". */
|
||||
if( (pRight = pExpr->pRight)->op==TK_ID ){
|
||||
int rc = resolveExprStep(pWalker, pRight);
|
||||
if( rc==WRC_Abort ) return WRC_Abort;
|
||||
if( pRight->op==TK_TRUEFALSE ){
|
||||
pExpr->op2 = pExpr->op;
|
||||
pExpr->op = TK_TRUTH;
|
||||
return WRC_Continue;
|
||||
}
|
||||
}
|
||||
/* Fall thru */
|
||||
}
|
||||
case TK_BETWEEN:
|
||||
case TK_EQ:
|
||||
case TK_NE:
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
case TK_GE:
|
||||
case TK_IS:
|
||||
case TK_ISNOT: {
|
||||
case TK_GE: {
|
||||
int nLeft, nRight;
|
||||
if( pParse->db->mallocFailed ) break;
|
||||
assert( pExpr->pLeft!=0 );
|
||||
@@ -893,8 +1018,8 @@ static int resolveOrderByTermToExprList(
|
||||
memset(&nc, 0, sizeof(nc));
|
||||
nc.pParse = pParse;
|
||||
nc.pSrcList = pSelect->pSrc;
|
||||
nc.pEList = pEList;
|
||||
nc.ncFlags = NC_AllowAgg;
|
||||
nc.uNC.pEList = pEList;
|
||||
nc.ncFlags = NC_AllowAgg|NC_UEList;
|
||||
nc.nErr = 0;
|
||||
db = pParse->db;
|
||||
savedSuppErr = db->suppressErr;
|
||||
@@ -959,12 +1084,10 @@ static int resolveCompoundOrderBy(
|
||||
pOrderBy = pSelect->pOrderBy;
|
||||
if( pOrderBy==0 ) return 0;
|
||||
db = pParse->db;
|
||||
#if SQLITE_MAX_COLUMN
|
||||
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
||||
sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
pOrderBy->a[i].done = 0;
|
||||
}
|
||||
@@ -1056,12 +1179,10 @@ int sqlite3ResolveOrderGroupBy(
|
||||
struct ExprList_item *pItem;
|
||||
|
||||
if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
|
||||
#if SQLITE_MAX_COLUMN
|
||||
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
||||
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
pEList = pSelect->pEList;
|
||||
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
|
||||
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
||||
@@ -1143,6 +1264,19 @@ static int resolveOrderGroupBy(
|
||||
}
|
||||
for(j=0; j<pSelect->pEList->nExpr; j++){
|
||||
if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pE->pWin ){
|
||||
/* Since this window function is being changed into a reference
|
||||
** to the same window function the result set, remove the instance
|
||||
** of this window function from the Select.pWin list. */
|
||||
Window **pp;
|
||||
for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
|
||||
if( *pp==pE->pWin ){
|
||||
*pp = (*pp)->pNextWin;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pItem->u.x.iOrderByCol = j+1;
|
||||
}
|
||||
}
|
||||
@@ -1199,8 +1333,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
*/
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
|
||||
sqlite3ResolveExprNames(&sNC, p->pOffset) ){
|
||||
sNC.pWinSelect = p;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pLimit) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
|
||||
@@ -1248,12 +1382,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
|
||||
** resolve the result-set expression list.
|
||||
*/
|
||||
sNC.ncFlags = NC_AllowAgg;
|
||||
sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
|
||||
sNC.pSrcList = p->pSrc;
|
||||
sNC.pNext = pOuterNC;
|
||||
|
||||
/* Resolve names in the result set. */
|
||||
if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
|
||||
sNC.ncFlags &= ~NC_AllowWin;
|
||||
|
||||
/* If there are no aggregate functions in the result-set, and no GROUP BY
|
||||
** expression, do not allow aggregates in any of the other expressions.
|
||||
@@ -1282,7 +1417,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
** Minor point: If this is the case, then the expression will be
|
||||
** re-evaluated for each reference to it.
|
||||
*/
|
||||
sNC.pEList = p->pEList;
|
||||
assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
|
||||
sNC.uNC.pEList = p->pEList;
|
||||
sNC.ncFlags |= NC_UEList;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
|
||||
|
||||
@@ -1300,7 +1437,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
** outer queries
|
||||
*/
|
||||
sNC.pNext = 0;
|
||||
sNC.ncFlags |= NC_AllowAgg;
|
||||
sNC.ncFlags |= NC_AllowAgg|NC_AllowWin;
|
||||
|
||||
/* If this is a converted compound query, move the ORDER BY clause from
|
||||
** the sub-query back to the parent query. At this point each term
|
||||
@@ -1331,6 +1468,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
if( db->mallocFailed ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
sNC.ncFlags &= ~NC_AllowWin;
|
||||
|
||||
/* Resolve the GROUP BY clause. At the same time, make sure
|
||||
** the GROUP BY clause does not contain aggregate functions.
|
||||
@@ -1515,7 +1653,7 @@ void sqlite3ResolveSelfReference(
|
||||
Table *pTab, /* The table being referenced */
|
||||
int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
|
||||
Expr *pExpr, /* Expression to resolve. May be NULL. */
|
||||
ExprList *pList /* Expression list to resolve. May be NUL. */
|
||||
ExprList *pList /* Expression list to resolve. May be NULL. */
|
||||
){
|
||||
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
|
||||
NameContext sNC; /* Name context for pParse->pNewTable */
|
||||
|
||||
+28
-24
@@ -124,30 +124,23 @@ struct RowSet {
|
||||
#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
|
||||
|
||||
/*
|
||||
** Turn bulk memory into a RowSet object. N bytes of memory
|
||||
** are available at pSpace. The db pointer is used as a memory context
|
||||
** for any subsequent allocations that need to occur.
|
||||
** Return a pointer to the new RowSet object.
|
||||
**
|
||||
** It must be the case that N is sufficient to make a Rowset. If not
|
||||
** an assertion fault occurs.
|
||||
**
|
||||
** If N is larger than the minimum, use the surplus as an initial
|
||||
** allocation of entries available to be filled.
|
||||
** Allocate a RowSet object. Return NULL if a memory allocation
|
||||
** error occurs.
|
||||
*/
|
||||
RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
|
||||
RowSet *p;
|
||||
assert( N >= ROUND8(sizeof(*p)) );
|
||||
p = pSpace;
|
||||
p->pChunk = 0;
|
||||
p->db = db;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pForest = 0;
|
||||
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
||||
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
||||
p->rsFlags = ROWSET_SORTED;
|
||||
p->iBatch = 0;
|
||||
RowSet *sqlite3RowSetInit(sqlite3 *db){
|
||||
RowSet *p = sqlite3DbMallocRawNN(db, sizeof(*p));
|
||||
if( p ){
|
||||
int N = sqlite3DbMallocSize(db, p);
|
||||
p->pChunk = 0;
|
||||
p->db = db;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pForest = 0;
|
||||
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
||||
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
||||
p->rsFlags = ROWSET_SORTED;
|
||||
p->iBatch = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -156,7 +149,8 @@ RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
|
||||
** the RowSet has allocated over its lifetime. This routine is
|
||||
** the destructor for the RowSet.
|
||||
*/
|
||||
void sqlite3RowSetClear(RowSet *p){
|
||||
void sqlite3RowSetClear(void *pArg){
|
||||
RowSet *p = (RowSet*)pArg;
|
||||
struct RowSetChunk *pChunk, *pNextChunk;
|
||||
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
|
||||
pNextChunk = pChunk->pNextChunk;
|
||||
@@ -170,6 +164,16 @@ void sqlite3RowSetClear(RowSet *p){
|
||||
p->rsFlags = ROWSET_SORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate all chunks from a RowSet. This frees all memory that
|
||||
** the RowSet has allocated over its lifetime. This routine is
|
||||
** the destructor for the RowSet.
|
||||
*/
|
||||
void sqlite3RowSetDelete(void *pArg){
|
||||
sqlite3RowSetClear(pArg);
|
||||
sqlite3DbFree(((RowSet*)pArg)->db, pArg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a new RowSetEntry object that is associated with the
|
||||
** given RowSet. Return a pointer to the new and completely uninitialized
|
||||
|
||||
+1377
-815
File diff suppressed because it is too large
Load Diff
-8398
File diff suppressed because it is too large
Load Diff
+2334
-540
File diff suppressed because it is too large
Load Diff
+810
-226
File diff suppressed because it is too large
Load Diff
+50
-5
@@ -134,7 +134,7 @@ struct sqlite3_api_routines {
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
@@ -246,7 +246,7 @@ struct sqlite3_api_routines {
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
@@ -292,6 +292,30 @@ struct sqlite3_api_routines {
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -418,7 +442,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
@@ -442,7 +466,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
@@ -518,7 +542,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
@@ -558,6 +582,27 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
||||
+423
-222
File diff suppressed because it is too large
Load Diff
+33
-4
@@ -122,7 +122,6 @@ void sqlite3StatusHighwater(int op, int X){
|
||||
: sqlite3MallocMutex()) );
|
||||
assert( op==SQLITE_STATUS_MALLOC_SIZE
|
||||
|| op==SQLITE_STATUS_PAGECACHE_SIZE
|
||||
|| op==SQLITE_STATUS_SCRATCH_SIZE
|
||||
|| op==SQLITE_STATUS_PARSER_STACK );
|
||||
if( newValue>wsdStat.mxValue[op] ){
|
||||
wsdStat.mxValue[op] = newValue;
|
||||
@@ -171,6 +170,28 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of LookasideSlot elements on the linked list
|
||||
*/
|
||||
static u32 countLookasideSlots(LookasideSlot *p){
|
||||
u32 cnt = 0;
|
||||
while( p ){
|
||||
p = p->pNext;
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Count the number of slots of lookaside memory that are outstanding
|
||||
*/
|
||||
int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
|
||||
u32 nInit = countLookasideSlots(db->lookaside.pInit);
|
||||
u32 nFree = countLookasideSlots(db->lookaside.pFree);
|
||||
if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit;
|
||||
return db->lookaside.nSlot - (nInit+nFree);
|
||||
}
|
||||
|
||||
/*
|
||||
** Query status information for a single database connection
|
||||
*/
|
||||
@@ -190,10 +211,15 @@ int sqlite3_db_status(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
switch( op ){
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
|
||||
*pCurrent = db->lookaside.nOut;
|
||||
*pHighwater = db->lookaside.mxOut;
|
||||
*pCurrent = sqlite3LookasideUsed(db, pHighwater);
|
||||
if( resetFlag ){
|
||||
db->lookaside.mxOut = db->lookaside.nOut;
|
||||
LookasideSlot *p = db->lookaside.pFree;
|
||||
if( p ){
|
||||
while( p->pNext ) p = p->pNext;
|
||||
p->pNext = db->lookaside.pInit;
|
||||
db->lookaside.pInit = db->lookaside.pFree;
|
||||
db->lookaside.pFree = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -311,6 +337,9 @@ int sqlite3_db_status(
|
||||
** pagers the database handle is connected to. *pHighwater is always set
|
||||
** to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_SPILL:
|
||||
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
|
||||
/* Fall through into the next case */
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS:
|
||||
case SQLITE_DBSTATUS_CACHE_WRITE:{
|
||||
|
||||
+265
-789
File diff suppressed because it is too large
Load Diff
+181
-3
@@ -2256,6 +2256,27 @@ static int SQLITE_TCLAPI test_config_sqllog(
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_sorterref
|
||||
**
|
||||
** Set the SQLITE_CONFIG_SORTERREF_SIZE configuration option
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_config_sorterref(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int iVal;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &iVal) ) return TCL_ERROR;
|
||||
sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, iVal);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: vfs_current_time_int64
|
||||
**
|
||||
@@ -2372,6 +2393,8 @@ static int SQLITE_TCLAPI test_snapshot_open(
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -2553,6 +2576,46 @@ static int SQLITE_TCLAPI test_delete_database(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: atomic_batch_write PATH
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_atomic_batch_write(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char *zFile = 0; /* Path to file to test */
|
||||
sqlite3 *db = 0; /* Database handle */
|
||||
sqlite3_file *pFd = 0; /* SQLite fd open on zFile */
|
||||
int bRes = 0; /* Integer result of this command */
|
||||
int dc = 0; /* Device-characteristics mask */
|
||||
int rc; /* sqlite3_open() return code */
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATH");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFile = Tcl_GetString(objv[1]);
|
||||
|
||||
rc = sqlite3_open(zFile, &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(db), 0);
|
||||
sqlite3_close(db);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
|
||||
dc = pFd->pMethods->xDeviceCharacteristics(pFd);
|
||||
if( dc & SQLITE_IOCAP_BATCH_ATOMIC ){
|
||||
bRes = 1;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(bRes));
|
||||
sqlite3_close(db);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_next_stmt DB STMT
|
||||
**
|
||||
@@ -4519,6 +4582,35 @@ static int SQLITE_TCLAPI test_complete16(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_normalize SQL
|
||||
**
|
||||
** Return the normalized value for an SQL statement.
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_normalize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char *zSql;
|
||||
char *zNorm;
|
||||
extern char *sqlite3_normalize(const char*);
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SQL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zSql = (char*)Tcl_GetString(objv[1]);
|
||||
zNorm = sqlite3_normalize(zSql);
|
||||
if( zNorm ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zNorm, -1));
|
||||
sqlite3_free(zNorm);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_step STMT
|
||||
**
|
||||
@@ -5594,6 +5686,44 @@ static int SQLITE_TCLAPI file_control_lasterrno_test(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_data_version DB DBNAME
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control with the
|
||||
** SQLITE_FCNTL_DATA_VERSION opcode, returning the result.
|
||||
*/
|
||||
static int SQLITE_TCLAPI file_control_data_version(
|
||||
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 */
|
||||
){
|
||||
unsigned int iVers; /* data version */
|
||||
char *zDb; /* Db name ("main", "temp" etc.) */
|
||||
sqlite3 *db; /* Database handle */
|
||||
int rc; /* file_control() return code */
|
||||
char zBuf[100];
|
||||
|
||||
if( objc!=3 && objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB [DBNAME]");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zDb = objc==3 ? Tcl_GetString(objv[2]) : NULL;
|
||||
|
||||
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_DATA_VERSION, (void *)&iVers);
|
||||
if( rc ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%u",iVers);
|
||||
Tcl_SetResult(interp, (char *)zBuf, TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_chunksize_test DB DBNAME SIZE
|
||||
**
|
||||
@@ -6854,17 +6984,16 @@ static int SQLITE_TCLAPI optimization_control(
|
||||
{ "all", SQLITE_AllOpts },
|
||||
{ "none", 0 },
|
||||
{ "query-flattener", SQLITE_QueryFlattener },
|
||||
{ "column-cache", SQLITE_ColumnCache },
|
||||
{ "groupby-order", SQLITE_GroupByOrder },
|
||||
{ "factor-constants", SQLITE_FactorOutConst },
|
||||
{ "distinct-opt", SQLITE_DistinctOpt },
|
||||
{ "cover-idx-scan", SQLITE_CoverIdxScan },
|
||||
{ "order-by-idx-join", SQLITE_OrderByIdxJoin },
|
||||
{ "transitive", SQLITE_Transitive },
|
||||
{ "subquery-coroutine", SQLITE_SubqCoroutine },
|
||||
{ "omit-noop-join", SQLITE_OmitNoopJoin },
|
||||
{ "stat3", SQLITE_Stat34 },
|
||||
{ "stat4", SQLITE_Stat34 },
|
||||
{ "skip-scan", SQLITE_SkipScan },
|
||||
};
|
||||
|
||||
if( objc!=4 ){
|
||||
@@ -6921,6 +7050,9 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
#endif
|
||||
static const struct {
|
||||
const char *zExtName;
|
||||
int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
@@ -6942,6 +7074,9 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
{ "totype", sqlite3_totype_init },
|
||||
{ "unionvtab", sqlite3_unionvtab_init },
|
||||
{ "wholenumber", sqlite3_wholenumber_init },
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
{ "zipfile", sqlite3_zipfile_init },
|
||||
#endif
|
||||
};
|
||||
sqlite3 *db;
|
||||
const char *zName;
|
||||
@@ -7323,6 +7458,8 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
|
||||
{ "LOAD_EXTENSION", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
|
||||
{ "NO_CKPT_ON_CLOSE",SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
|
||||
{ "QPSG", SQLITE_DBCONFIG_ENABLE_QPSG },
|
||||
{ "TRIGGER_EQP", SQLITE_DBCONFIG_TRIGGER_EQP },
|
||||
{ "RESET_DB", SQLITE_DBCONFIG_RESET_DATABASE },
|
||||
};
|
||||
int i;
|
||||
int v;
|
||||
@@ -7375,6 +7512,35 @@ static int SQLITE_TCLAPI test_dbconfig_maindbname_icecube(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mmap_warm DB DBNAME
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_mmap_warm(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern int sqlite3_mmap_warm(sqlite3 *db, const char *);
|
||||
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB ?DBNAME?");
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
const char *zDb = 0;
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
if( objc==3 ){
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
}
|
||||
rc = sqlite3_mmap_warm(db, zDb);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
@@ -7473,6 +7639,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_open16", test_open16 ,0 },
|
||||
{ "sqlite3_open_v2", test_open_v2 ,0 },
|
||||
{ "sqlite3_complete16", test_complete16 ,0 },
|
||||
{ "sqlite3_normalize", test_normalize ,0 },
|
||||
|
||||
{ "sqlite3_prepare", test_prepare ,0 },
|
||||
{ "sqlite3_prepare16", test_prepare16 ,0 },
|
||||
@@ -7572,6 +7739,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
|
||||
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
|
||||
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
|
||||
{ "file_control_data_version", file_control_data_version, 0 },
|
||||
#if SQLITE_OS_WIN
|
||||
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
|
||||
{ "file_control_win32_get_handle", file_control_win32_get_handle, 0 },
|
||||
@@ -7644,7 +7812,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 },
|
||||
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
|
||||
#endif
|
||||
{ "sqlite3_delete_database", test_delete_database, 0 },
|
||||
{ "sqlite3_delete_database", test_delete_database, 0 },
|
||||
{ "atomic_batch_write", test_atomic_batch_write, 0 },
|
||||
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
|
||||
{ "sqlite3_config_sorterref", test_config_sorterref, 0 },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
|
||||
@@ -7668,6 +7839,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
extern int sqlite3_fts3_enable_parentheses;
|
||||
#endif
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
extern int sqlite3SelectTrace;
|
||||
#endif
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
@@ -7754,6 +7928,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
(char*)&sqlite3_sync_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_fullsync_count",
|
||||
(char*)&sqlite3_fullsync_count, TCL_LINK_INT);
|
||||
#if defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
Tcl_LinkVar(interp, "sqlite3SelectTrace",
|
||||
(char*)&sqlite3SelectTrace, TCL_LINK_INT);
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST)
|
||||
Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses",
|
||||
(char*)&sqlite3_fts3_enable_parentheses, TCL_LINK_INT);
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ static int SQLITE_TCLAPI btree_begin_transaction(
|
||||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 1);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
|
||||
|
||||
+25
@@ -736,6 +736,7 @@ static int processDevSymArgs(
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
|
||||
{ "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -976,7 +977,30 @@ static int SQLITE_TCLAPI devSymObjCmd(
|
||||
devsym_register(iDc, iSectorSize);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_crash_on_write N
|
||||
*/
|
||||
static int SQLITE_TCLAPI writeCrashObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void devsym_crash_on_write(int);
|
||||
int nWrite = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
devsym_crash_on_write(nWrite);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1068,6 +1092,7 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
|
||||
|
||||
+11
-10
@@ -897,17 +897,18 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
case SQLITE_INDEX_CONSTRAINT_REGEXP:
|
||||
zOp = "regexp"; break;
|
||||
}
|
||||
if( zOp[0]=='L' ){
|
||||
zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')",
|
||||
zSep, zNewCol);
|
||||
} else {
|
||||
zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zNewCol, zOp);
|
||||
if( zOp ){
|
||||
if( zOp[0]=='L' ){
|
||||
zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')",
|
||||
zSep, zNewCol);
|
||||
} else {
|
||||
zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zNewCol, zOp);
|
||||
}
|
||||
string_concat(&zQuery, zNew, 1, &rc);
|
||||
zSep = "AND";
|
||||
pUsage->argvIndex = ++nArg;
|
||||
pUsage->omit = 1;
|
||||
}
|
||||
string_concat(&zQuery, zNew, 1, &rc);
|
||||
|
||||
zSep = "AND";
|
||||
pUsage->argvIndex = ++nArg;
|
||||
pUsage->omit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,16 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
zOp = "glob"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_REGEXP:
|
||||
zOp = "regexp"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_NE:
|
||||
zOp = "ne"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOT:
|
||||
zOp = "isnot"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
|
||||
zOp = "isnotnull"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNULL:
|
||||
zOp = "isnull"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_IS:
|
||||
zOp = "is"; break;
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
|
||||
|
||||
@@ -148,6 +148,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "hiddencolumns", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "deserialize", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "deserialize", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMSYS3
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mem3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -160,6 +166,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "offset_sql_func","1",TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "offset_sql_func","0",TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -214,6 +226,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "json1", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "has_codec", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "has_codec", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "like_match_blobs", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -423,6 +441,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "icu", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU_COLLATIONS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "icu_collations", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "icu_collations", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_INCRBLOB
|
||||
Tcl_SetVar2(interp, "sqlite_options", "incrblob", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -483,6 +507,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
|
||||
#ifdef SQLITE_ENABLE_NULL_TRIM
|
||||
Tcl_SetVar2(interp, "sqlite_options", "null_trim", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "null_trim", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_OR_OPTIMIZATION
|
||||
Tcl_SetVar2(interp, "sqlite_options", "or_opt", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -690,6 +720,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "unlock_notify", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_FAST_SECURE_DELETE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fast_secure_delete", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fast_secure_delete", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_SECURE_DELETE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -726,6 +762,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_WINDOWFUNC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "windowfunc", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "windowfunc", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#define LINKVAR(x) { \
|
||||
static const int cv_ ## x = SQLITE_ ## x; \
|
||||
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
|
||||
|
||||
+168
-50
@@ -28,6 +28,7 @@
|
||||
** Name used to identify this VFS.
|
||||
*/
|
||||
#define DEVSYM_VFS_NAME "devsym"
|
||||
#define WRITECRASH_NAME "writecrash"
|
||||
|
||||
typedef struct devsym_file devsym_file;
|
||||
struct devsym_file {
|
||||
@@ -72,61 +73,13 @@ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int devsymSleep(sqlite3_vfs*, int microseconds);
|
||||
static int devsymCurrentTime(sqlite3_vfs*, double*);
|
||||
|
||||
static sqlite3_vfs devsym_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
DEVSYM_VFS_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
devsymOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static sqlite3_io_methods devsym_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
devsymWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
devsymSectorSize, /* xSectorSize */
|
||||
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
struct DevsymGlobal {
|
||||
sqlite3_vfs *pVfs;
|
||||
int iDeviceChar;
|
||||
int iSectorSize;
|
||||
int nWriteCrash;
|
||||
};
|
||||
struct DevsymGlobal g = {0, 0, 512};
|
||||
struct DevsymGlobal g = {0, 0, 512, 0};
|
||||
|
||||
/*
|
||||
** Close an devsym-file.
|
||||
@@ -271,6 +224,26 @@ static int devsymOpen(
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
static sqlite3_io_methods devsym_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
devsymWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
devsymSectorSize, /* xSectorSize */
|
||||
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
int rc;
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
@@ -372,6 +345,137 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an writecrash-file.
|
||||
*/
|
||||
static int writecrashSectorSize(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsSectorSize(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an writecrash-file.
|
||||
*/
|
||||
static int writecrashDeviceCharacteristics(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsDeviceCharacteristics(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an writecrash-file.
|
||||
*/
|
||||
static int writecrashWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
if( g.nWriteCrash>0 ){
|
||||
g.nWriteCrash--;
|
||||
if( g.nWriteCrash==0 ) abort();
|
||||
}
|
||||
return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an writecrash file handle.
|
||||
*/
|
||||
static int writecrashOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
static sqlite3_io_methods writecrash_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
writecrashWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
writecrashSectorSize, /* xSectorSize */
|
||||
writecrashDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
int rc;
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
|
||||
if( p->pReal->pMethods ){
|
||||
pFile->pMethods = &writecrash_io_methods;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static sqlite3_vfs devsym_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
DEVSYM_VFS_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
devsymOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static sqlite3_vfs writecrash_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
WRITECRASH_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
writecrashOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** This procedure registers the devsym vfs with SQLite. If the argument is
|
||||
@@ -379,10 +483,13 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
** available function in this file.
|
||||
*/
|
||||
void devsym_register(int iDeviceChar, int iSectorSize){
|
||||
|
||||
if( g.pVfs==0 ){
|
||||
g.pVfs = sqlite3_vfs_find(0);
|
||||
devsym_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
sqlite3_vfs_register(&devsym_vfs, 0);
|
||||
sqlite3_vfs_register(&writecrash_vfs, 0);
|
||||
}
|
||||
if( iDeviceChar>=0 ){
|
||||
g.iDeviceChar = iDeviceChar;
|
||||
@@ -403,4 +510,15 @@ void devsym_unregister(){
|
||||
g.iSectorSize = 0;
|
||||
}
|
||||
|
||||
void devsym_crash_on_write(int nWrite){
|
||||
if( g.pVfs==0 ){
|
||||
g.pVfs = sqlite3_vfs_find(0);
|
||||
devsym_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
sqlite3_vfs_register(&devsym_vfs, 0);
|
||||
sqlite3_vfs_register(&writecrash_vfs, 0);
|
||||
}
|
||||
g.nWriteCrash = nWrite;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+118
@@ -791,6 +791,123 @@ abuse_err:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** SQLite user defined function to use with matchinfo() to calculate the
|
||||
** relevancy of an FTS match. The value returned is the relevancy score
|
||||
** (a real value greater than or equal to zero). A larger value indicates
|
||||
** a more relevant document.
|
||||
**
|
||||
** The overall relevancy returned is the sum of the relevancies of each
|
||||
** column value in the FTS table. The relevancy of a column value is the
|
||||
** sum of the following for each reportable phrase in the FTS query:
|
||||
**
|
||||
** (<hit count> / <global hit count>) * <column weight>
|
||||
**
|
||||
** where <hit count> is the number of instances of the phrase in the
|
||||
** column value of the current row and <global hit count> is the number
|
||||
** of instances of the phrase in the same column of all rows in the FTS
|
||||
** table. The <column weight> is a weighting factor assigned to each
|
||||
** column by the caller (see below).
|
||||
**
|
||||
** The first argument to this function must be the return value of the FTS
|
||||
** matchinfo() function. Following this must be one argument for each column
|
||||
** of the FTS table containing a numeric weight factor for the corresponding
|
||||
** column. Example:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE documents USING fts3(title, content)
|
||||
**
|
||||
** The following query returns the docids of documents that match the full-text
|
||||
** query <query> sorted from most to least relevant. When calculating
|
||||
** relevance, query term instances in the 'title' column are given twice the
|
||||
** weighting of those in the 'content' column.
|
||||
**
|
||||
** SELECT docid FROM documents
|
||||
** WHERE documents MATCH <query>
|
||||
** ORDER BY rank(matchinfo(documents), 1.0, 0.5) DESC
|
||||
*/
|
||||
static void rankfunc(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
|
||||
int *aMatchinfo; /* Return value of matchinfo() */
|
||||
int nMatchinfo; /* Number of elements in aMatchinfo[] */
|
||||
int nCol = 0; /* Number of columns in the table */
|
||||
int nPhrase = 0; /* Number of phrases in the query */
|
||||
int iPhrase; /* Current phrase */
|
||||
double score = 0.0; /* Value to return */
|
||||
|
||||
assert( sizeof(int)==4 );
|
||||
|
||||
/* Check that the number of arguments passed to this function is correct.
|
||||
** If not, jump to wrong_number_args. Set aMatchinfo to point to the array
|
||||
** of unsigned integer values returned by FTS function matchinfo. Set
|
||||
** nPhrase to contain the number of reportable phrases in the users full-text
|
||||
** query, and nCol to the number of columns in the table. Then check that the
|
||||
** size of the matchinfo blob is as expected. Return an error if it is not.
|
||||
*/
|
||||
if( nVal<1 ) goto wrong_number_args;
|
||||
aMatchinfo = (int*)sqlite3_value_blob(apVal[0]);
|
||||
nMatchinfo = sqlite3_value_bytes(apVal[0]) / sizeof(int);
|
||||
if( nMatchinfo>=2 ){
|
||||
nPhrase = aMatchinfo[0];
|
||||
nCol = aMatchinfo[1];
|
||||
}
|
||||
if( nMatchinfo!=(2+3*nCol*nPhrase) ){
|
||||
sqlite3_result_error(pCtx,
|
||||
"invalid matchinfo blob passed to function rank()", -1);
|
||||
return;
|
||||
}
|
||||
if( nVal!=(1+nCol) ) goto wrong_number_args;
|
||||
|
||||
/* Iterate through each phrase in the users query. */
|
||||
for(iPhrase=0; iPhrase<nPhrase; iPhrase++){
|
||||
int iCol; /* Current column */
|
||||
|
||||
/* Now iterate through each column in the users query. For each column,
|
||||
** increment the relevancy score by:
|
||||
**
|
||||
** (<hit count> / <global hit count>) * <column weight>
|
||||
**
|
||||
** aPhraseinfo[] points to the start of the data for phrase iPhrase. So
|
||||
** the hit count and global hit counts for each column are found in
|
||||
** aPhraseinfo[iCol*3] and aPhraseinfo[iCol*3+1], respectively.
|
||||
*/
|
||||
int *aPhraseinfo = &aMatchinfo[2 + iPhrase*nCol*3];
|
||||
for(iCol=0; iCol<nCol; iCol++){
|
||||
int nHitCount = aPhraseinfo[3*iCol];
|
||||
int nGlobalHitCount = aPhraseinfo[3*iCol+1];
|
||||
double weight = sqlite3_value_double(apVal[iCol+1]);
|
||||
if( nHitCount>0 ){
|
||||
score += ((double)nHitCount / (double)nGlobalHitCount) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_double(pCtx, score);
|
||||
return;
|
||||
|
||||
/* Jump here if the wrong number of arguments are passed to this function */
|
||||
wrong_number_args:
|
||||
sqlite3_result_error(pCtx, "wrong number of arguments to function rank()", -1);
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI install_fts3_rank_function(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
sqlite3 *db;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
sqlite3_create_function(db, "rank", -1, SQLITE_UTF8, 0, rankfunc, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
@@ -801,6 +918,7 @@ int Sqlitetest_func_Init(Tcl_Interp *interp){
|
||||
} aObjCmd[] = {
|
||||
{ "autoinstall_test_functions", autoinstall_test_funcs },
|
||||
{ "abuse_create_function", abuse_create_function },
|
||||
{ "install_fts3_rank_function", install_fts3_rank_function },
|
||||
};
|
||||
int i;
|
||||
extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
|
||||
+19
-41
@@ -32,6 +32,8 @@ static struct MemFault {
|
||||
int nRepeat; /* Number of times to repeat the failure */
|
||||
int nBenign; /* Number of benign failures seen since last config */
|
||||
int nFail; /* Number of failures seen since last config */
|
||||
int nOkBefore; /* Successful allocations prior to the first fault */
|
||||
int nOkAfter; /* Successful allocations after a fault */
|
||||
u8 enable; /* True if enabled */
|
||||
int isInstalled; /* True if the fault simulation layer is installed */
|
||||
int isBenignMode; /* True if malloc failures are considered benign */
|
||||
@@ -47,18 +49,32 @@ static void sqlite3Fault(void){
|
||||
cnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine exists as a place to set a breakpoint that will
|
||||
** fire the first time any malloc() fails on a single test case.
|
||||
** The sqlite3Fault() routine above runs on every malloc() failure.
|
||||
** This routine only runs on the first such failure.
|
||||
*/
|
||||
static void sqlite3FirstFault(void){
|
||||
static int cnt2 = 0;
|
||||
cnt2++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if a fault should be simulated. Return true to simulate
|
||||
** the fault. Return false if the fault should not be simulated.
|
||||
*/
|
||||
static int faultsimStep(void){
|
||||
if( likely(!memfault.enable) ){
|
||||
memfault.nOkAfter++;
|
||||
return 0;
|
||||
}
|
||||
if( memfault.iCountdown>0 ){
|
||||
memfault.iCountdown--;
|
||||
memfault.nOkBefore++;
|
||||
return 0;
|
||||
}
|
||||
if( memfault.nFail==0 ) sqlite3FirstFault();
|
||||
sqlite3Fault();
|
||||
memfault.nFail++;
|
||||
if( memfault.isBenignMode>0 ){
|
||||
@@ -133,6 +149,8 @@ static void faultsimConfig(int nDelay, int nRepeat){
|
||||
memfault.nRepeat = nRepeat;
|
||||
memfault.nBenign = 0;
|
||||
memfault.nFail = 0;
|
||||
memfault.nOkBefore = 0;
|
||||
memfault.nOkAfter = 0;
|
||||
memfault.enable = nDelay>=0;
|
||||
|
||||
/* Sometimes, when running multi-threaded tests, the isBenignMode
|
||||
@@ -887,46 +905,6 @@ static int SQLITE_TCLAPI test_memdebug_log(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_scratch SIZE N
|
||||
**
|
||||
** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
|
||||
** The buffer is static and is of limited size. N might be
|
||||
** adjusted downward as needed to accommodate the requested size.
|
||||
** The revised value of N is returned.
|
||||
**
|
||||
** A negative SIZE causes the buffer pointer to be NULL.
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_config_scratch(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int sz, N, rc;
|
||||
Tcl_Obj *pResult;
|
||||
static char *buf = 0;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
|
||||
free(buf);
|
||||
if( sz<0 ){
|
||||
buf = 0;
|
||||
rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, (void*)0, 0, 0);
|
||||
}else{
|
||||
buf = malloc( sz*N + 1 );
|
||||
rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
|
||||
}
|
||||
pResult = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
|
||||
Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
|
||||
Tcl_SetObjResult(interp, pResult);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_pagecache SIZE N
|
||||
**
|
||||
@@ -1423,6 +1401,7 @@ static int SQLITE_TCLAPI test_db_status(
|
||||
{ "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE },
|
||||
{ "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS },
|
||||
{ "CACHE_USED_SHARED", SQLITE_DBSTATUS_CACHE_USED_SHARED },
|
||||
{ "CACHE_SPILL", SQLITE_DBSTATUS_CACHE_SPILL },
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
@@ -1538,7 +1517,6 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 },
|
||||
{ "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 },
|
||||
{ "sqlite3_memdebug_log", test_memdebug_log ,0 },
|
||||
{ "sqlite3_config_scratch", test_config_scratch ,0 },
|
||||
{ "sqlite3_config_pagecache", test_config_pagecache ,0 },
|
||||
{ "sqlite3_config_alt_pcache", test_alt_pcache ,0 },
|
||||
{ "sqlite3_status", test_status ,0 },
|
||||
|
||||
+450
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
** 2017-10-13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code to implement an MD5 extension to TCL.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sqlite3.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If compiled on a machine that doesn't have a 32-bit integer,
|
||||
* you just set "uint32" to the appropriate datatype for an
|
||||
* unsigned 32-bit integer. For example:
|
||||
*
|
||||
* cc -Duint32='unsigned long' md5.c
|
||||
*
|
||||
*/
|
||||
#ifndef uint32
|
||||
# define uint32 unsigned int
|
||||
#endif
|
||||
|
||||
struct MD5Context {
|
||||
int isInit;
|
||||
uint32 buf[4];
|
||||
uint32 bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
typedef struct MD5Context MD5Context;
|
||||
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
static void byteReverse (unsigned char *buf, unsigned longs){
|
||||
uint32 t;
|
||||
do {
|
||||
t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
|
||||
((unsigned)buf[1]<<8 | buf[0]);
|
||||
*(uint32 *)buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
static void MD5Transform(uint32 buf[4], const uint32 in[16]){
|
||||
register uint32 a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
static void MD5Init(MD5Context *ctx){
|
||||
ctx->isInit = 1;
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
static
|
||||
void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
uint32 t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if ( t ) {
|
||||
unsigned char *p = (unsigned char *)ctx->in + t;
|
||||
|
||||
t = 64-t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count-8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
memcpy(ctx->in + 14*4, ctx->bits, 8);
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
byteReverse((unsigned char *)ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 128-bit MD5 digest into a 32-digit base-16 number.
|
||||
*/
|
||||
static void MD5DigestToBase16(unsigned char *digest, char *zBuf){
|
||||
static char const zEncode[] = "0123456789abcdef";
|
||||
int i, j;
|
||||
|
||||
for(j=i=0; i<16; i++){
|
||||
int a = digest[i];
|
||||
zBuf[j++] = zEncode[(a>>4)&0xf];
|
||||
zBuf[j++] = zEncode[a & 0xf];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers
|
||||
** each representing 16 bits of the digest and separated from each
|
||||
** other by a "-" character.
|
||||
*/
|
||||
static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){
|
||||
int i, j;
|
||||
unsigned int x;
|
||||
for(i=j=0; i<16; i+=2){
|
||||
x = digest[i]*256 + digest[i+1];
|
||||
if( i>0 ) zDigest[j++] = '-';
|
||||
sqlite3_snprintf(50-j, &zDigest[j], "%05u", x);
|
||||
j += 5;
|
||||
}
|
||||
zDigest[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command for md5. The argument is the text to be hashed. The
|
||||
** Result is the hash in base64.
|
||||
*/
|
||||
static int SQLITE_TCLAPI md5_cmd(
|
||||
void*cd,
|
||||
Tcl_Interp *interp,
|
||||
int argc,
|
||||
const char **argv
|
||||
){
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
char zBuf[50];
|
||||
void (*converter)(unsigned char*, char*);
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
|
||||
MD5Final(digest, &ctx);
|
||||
converter = (void(*)(unsigned char*,char*))cd;
|
||||
converter(digest, zBuf);
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command to take the md5 hash of a file. The argument is the
|
||||
** name of the file.
|
||||
*/
|
||||
static int SQLITE_TCLAPI md5file_cmd(
|
||||
void*cd,
|
||||
Tcl_Interp *interp,
|
||||
int argc,
|
||||
const char **argv
|
||||
){
|
||||
FILE *in;
|
||||
int ofst;
|
||||
int amt;
|
||||
MD5Context ctx;
|
||||
void (*converter)(unsigned char*, char*);
|
||||
unsigned char digest[16];
|
||||
char zBuf[10240];
|
||||
|
||||
if( argc!=2 && argc!=4 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME [OFFSET AMT]\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( argc==4 ){
|
||||
ofst = atoi(argv[2]);
|
||||
amt = atoi(argv[3]);
|
||||
}else{
|
||||
ofst = 0;
|
||||
amt = 2147483647;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(in, ofst, SEEK_SET);
|
||||
MD5Init(&ctx);
|
||||
while( amt>0 ){
|
||||
int n;
|
||||
n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in);
|
||||
if( n<=0 ) break;
|
||||
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
|
||||
amt -= n;
|
||||
}
|
||||
fclose(in);
|
||||
MD5Final(digest, &ctx);
|
||||
converter = (void(*)(unsigned char*,char*))cd;
|
||||
converter(digest, zBuf);
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the four new TCL commands for generating MD5 checksums
|
||||
** with the TCL interpreter.
|
||||
*/
|
||||
int Md5_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd,
|
||||
MD5DigestToBase16, 0);
|
||||
Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc*)md5_cmd,
|
||||
MD5DigestToBase10x8, 0);
|
||||
Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd,
|
||||
MD5DigestToBase16, 0);
|
||||
Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc*)md5file_cmd,
|
||||
MD5DigestToBase10x8, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** During testing, the special md5sum() aggregate function is available.
|
||||
** inside SQLite. The following routines implement that function.
|
||||
*/
|
||||
static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
MD5Context *p;
|
||||
int i;
|
||||
if( argc<1 ) return;
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( !p->isInit ){
|
||||
MD5Init(p);
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
const char *zData = (char*)sqlite3_value_text(argv[i]);
|
||||
if( zData ){
|
||||
MD5Update(p, (unsigned char*)zData, (int)strlen(zData));
|
||||
}
|
||||
}
|
||||
}
|
||||
static void md5finalize(sqlite3_context *context){
|
||||
MD5Context *p;
|
||||
unsigned char digest[16];
|
||||
char zBuf[33];
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
MD5Final(digest,p);
|
||||
MD5DigestToBase16(digest, zBuf);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
int Md5_Register(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pThunk
|
||||
){
|
||||
int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
||||
md5step, md5finalize);
|
||||
sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
** 2017-10-13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains extensions to the the "tclsqlite.c" module used for
|
||||
** testing. Basically, all of the other "test_*.c" modules are linked
|
||||
** into the enhanced tclsh used for testing (and named "testfixture" or
|
||||
** "testfixture.exe") using logic encoded by this file.
|
||||
**
|
||||
** The code in this file used to be found in tclsqlite3.c, contained within
|
||||
** #if SQLITE_TEST ... #endif. It is factored out into this separate module
|
||||
** in an effort to keep the tclsqlite.c file pure.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Needed for the setrlimit() system call on unix */
|
||||
#if defined(unix)
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
/* Forward declaration */
|
||||
static int SQLITE_TCLAPI load_testfixture_extensions(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
);
|
||||
|
||||
/*
|
||||
** This routine is the primary export of this file.
|
||||
**
|
||||
** Configure the interpreter passed as the first argument to have access
|
||||
** to the commands and linked variables that make up:
|
||||
**
|
||||
** * the [sqlite3] extension itself,
|
||||
**
|
||||
** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
|
||||
**
|
||||
** * If SQLITE_TEST is set, the various test interfaces used by the Tcl
|
||||
** test suite.
|
||||
*/
|
||||
const char *sqlite3TestInit(Tcl_Interp *interp){
|
||||
extern int Sqlite3_Init(Tcl_Interp*);
|
||||
extern int Sqliteconfig_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest1_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest2_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest3_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest4_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest5_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest6_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest7_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest8_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest9_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestasync_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_autoext_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_blob_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
|
||||
extern int Sqlitetest_func_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_hexio_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_init_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_malloc_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_mutex_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestschema_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestsse_Init(Tcl_Interp*);
|
||||
extern int Sqlitetesttclvar_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestfs_Init(Tcl_Interp*);
|
||||
extern int SqlitetestThread_Init(Tcl_Interp*);
|
||||
extern int SqlitetestOnefile_Init();
|
||||
extern int SqlitetestOsinst_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestbackup_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
||||
extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
||||
extern int Sqlitequota_Init(Tcl_Interp*);
|
||||
extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
||||
extern int SqliteSuperlock_Init(Tcl_Interp*);
|
||||
extern int SqlitetestSyscall_Init(Tcl_Interp*);
|
||||
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
extern int TestSession_Init(Tcl_Interp*);
|
||||
#endif
|
||||
extern int Md5_Init(Tcl_Interp*);
|
||||
extern int Fts5tcl_Init(Tcl_Interp *);
|
||||
extern int SqliteRbu_Init(Tcl_Interp*);
|
||||
extern int Sqlitetesttcl_Init(Tcl_Interp*);
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
extern int Zipvfs_Init(Tcl_Interp*);
|
||||
#endif
|
||||
extern int TestExpert_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_window_Init(Tcl_Interp *);
|
||||
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
|
||||
/* Since the primary use case for this binary is testing of SQLite,
|
||||
** be sure to generate core files if we crash */
|
||||
#if defined(unix)
|
||||
{ struct rlimit x;
|
||||
getrlimit(RLIMIT_CORE, &x);
|
||||
x.rlim_cur = x.rlim_max;
|
||||
setrlimit(RLIMIT_CORE, &x);
|
||||
}
|
||||
#endif /* unix */
|
||||
|
||||
if( Tcl_GetCommandInfo(interp, "sqlite3", &cmdInfo)==0 ){
|
||||
Sqlite3_Init(interp);
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
Zipvfs_Init(interp);
|
||||
#endif
|
||||
Md5_Init(interp);
|
||||
Sqliteconfig_Init(interp);
|
||||
Sqlitetest1_Init(interp);
|
||||
Sqlitetest2_Init(interp);
|
||||
Sqlitetest3_Init(interp);
|
||||
Sqlitetest4_Init(interp);
|
||||
Sqlitetest5_Init(interp);
|
||||
Sqlitetest6_Init(interp);
|
||||
Sqlitetest7_Init(interp);
|
||||
Sqlitetest8_Init(interp);
|
||||
Sqlitetest9_Init(interp);
|
||||
Sqlitetestasync_Init(interp);
|
||||
Sqlitetest_autoext_Init(interp);
|
||||
Sqlitetest_blob_Init(interp);
|
||||
Sqlitetest_demovfs_Init(interp);
|
||||
Sqlitetest_func_Init(interp);
|
||||
Sqlitetest_hexio_Init(interp);
|
||||
Sqlitetest_init_Init(interp);
|
||||
Sqlitetest_malloc_Init(interp);
|
||||
Sqlitetest_mutex_Init(interp);
|
||||
Sqlitetestschema_Init(interp);
|
||||
Sqlitetesttclvar_Init(interp);
|
||||
Sqlitetestfs_Init(interp);
|
||||
SqlitetestThread_Init(interp);
|
||||
SqlitetestOnefile_Init();
|
||||
SqlitetestOsinst_Init(interp);
|
||||
Sqlitetestbackup_Init(interp);
|
||||
Sqlitetestintarray_Init(interp);
|
||||
Sqlitetestvfs_Init(interp);
|
||||
Sqlitetestrtree_Init(interp);
|
||||
Sqlitequota_Init(interp);
|
||||
Sqlitemultiplex_Init(interp);
|
||||
SqliteSuperlock_Init(interp);
|
||||
SqlitetestSyscall_Init(interp);
|
||||
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
TestSession_Init(interp);
|
||||
#endif
|
||||
Fts5tcl_Init(interp);
|
||||
SqliteRbu_Init(interp);
|
||||
Sqlitetesttcl_Init(interp);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
Sqlitetestfts3_Init(interp);
|
||||
#endif
|
||||
TestExpert_Init(interp);
|
||||
Sqlitetest_window_Init(interp);
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "load_testfixture_extensions", load_testfixture_extensions,0,0
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* tclcmd: load_testfixture_extensions
|
||||
*/
|
||||
static int SQLITE_TCLAPI load_testfixture_extensions(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
|
||||
Tcl_Interp *slave;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SLAVE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1]));
|
||||
if( !slave ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
(void)sqlite3TestInit(slave);
|
||||
return TCL_OK;
|
||||
}
|
||||
+88
-2
@@ -15,6 +15,25 @@
|
||||
**
|
||||
** The emphasis of this file is a virtual table that provides
|
||||
** access to TCL variables.
|
||||
**
|
||||
** The TCLVAR eponymous virtual table has a schema like this:
|
||||
**
|
||||
** CREATE TABLE tclvar(
|
||||
** name TEXT, -- base name of the variable: "x" in "$x(y)"
|
||||
** arrayname TEXT, -- array index name: "y" in "$x(y)"
|
||||
** value TEXT, -- the value of the variable
|
||||
** fullname TEXT, -- the full name of the variable
|
||||
** PRIMARY KEY(fullname)
|
||||
** ) WITHOUT ROWID;
|
||||
**
|
||||
** DELETE, INSERT, and UPDATE operations use the "fullname" field to
|
||||
** determine the variable to be modified. Changing "value" to NULL
|
||||
** deletes the variable.
|
||||
**
|
||||
** For SELECT operations, the "name" and "arrayname" fields will always
|
||||
** match the "fullname" field. For DELETE, INSERT, and UPDATE, the
|
||||
** "name" and "arrayname" fields are ignored and the variable is modified
|
||||
** according to "fullname" and "value" only.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
@@ -67,7 +86,12 @@ static int tclvarConnect(
|
||||
){
|
||||
tclvar_vtab *pVtab;
|
||||
static const char zSchema[] =
|
||||
"CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
|
||||
"CREATE TABLE x("
|
||||
" name TEXT," /* Base name */
|
||||
" arrayname TEXT," /* Array index */
|
||||
" value TEXT," /* Value */
|
||||
" fullname TEXT PRIMARY KEY" /* base(index) name */
|
||||
") WITHOUT ROWID";
|
||||
pVtab = sqlite3MallocZero( sizeof(*pVtab) );
|
||||
if( pVtab==0 ) return SQLITE_NOMEM;
|
||||
*ppVtab = &pVtab->base;
|
||||
@@ -251,6 +275,16 @@ static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
char *z3;
|
||||
if( p2 ){
|
||||
z3 = sqlite3_mprintf("%s(%s)", z1, z2);
|
||||
sqlite3_result_text(ctx, z3, -1, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -376,6 +410,58 @@ static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table
|
||||
*/
|
||||
static int tclvarUpdate(
|
||||
sqlite3_vtab *tab,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
tclvar_vtab *pTab = (tclvar_vtab*)tab;
|
||||
if( argc==1 ){
|
||||
/* A DELETE operation. The variable to be deleted is stored in argv[0] */
|
||||
const char *zVar = (const char*)sqlite3_value_text(argv[0]);
|
||||
Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||||
/* An INSERT operation */
|
||||
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
|
||||
const char *zName;
|
||||
if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){
|
||||
tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
zName = (const char*)sqlite3_value_text(argv[5]);
|
||||
if( zValue ){
|
||||
Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY);
|
||||
}else{
|
||||
Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_TEXT
|
||||
&& sqlite3_value_type(argv[1])==SQLITE_TEXT
|
||||
){
|
||||
/* An UPDATE operation */
|
||||
const char *zOldName = (const char*)sqlite3_value_text(argv[0]);
|
||||
const char *zNewName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
|
||||
|
||||
if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){
|
||||
Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
if( zValue!=0 ){
|
||||
Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
@@ -394,7 +480,7 @@ static sqlite3_module tclvarModule = {
|
||||
tclvarEof, /* xEof - check for end of scan */
|
||||
tclvarColumn, /* xColumn - read data */
|
||||
tclvarRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
tclvarUpdate, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
|
||||
+30
-4
@@ -133,8 +133,9 @@ struct Testvfs {
|
||||
#define TESTVFS_UNLOCK_MASK 0x00020000
|
||||
#define TESTVFS_LOCK_MASK 0x00040000
|
||||
#define TESTVFS_CKLOCK_MASK 0x00080000
|
||||
#define TESTVFS_FCNTL_MASK 0x00100000
|
||||
|
||||
#define TESTVFS_ALL_MASK 0x000FFFFF
|
||||
#define TESTVFS_ALL_MASK 0x001FFFFF
|
||||
|
||||
|
||||
#define TESTVFS_MAX_PAGES 1024
|
||||
@@ -517,7 +518,8 @@ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
** File control method. For custom operations on an tvfs-file.
|
||||
*/
|
||||
static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
TestvfsFd *p = tvfsGetFd(pFile);
|
||||
TestvfsFd *pFd = tvfsGetFd(pFile);
|
||||
Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
|
||||
if( op==SQLITE_FCNTL_PRAGMA ){
|
||||
char **argv = (char**)pArg;
|
||||
if( sqlite3_stricmp(argv[1],"error")==0 ){
|
||||
@@ -535,11 +537,34 @@ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
return rc;
|
||||
}
|
||||
if( sqlite3_stricmp(argv[1], "filename")==0 ){
|
||||
argv[0] = sqlite3_mprintf("%s", p->zFilename);
|
||||
argv[0] = sqlite3_mprintf("%s", pFd->zFilename);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return sqlite3OsFileControl(p->pReal, op, pArg);
|
||||
if( p->pScript && (p->mask&TESTVFS_FCNTL_MASK) ){
|
||||
struct Fcntl {
|
||||
int iFnctl;
|
||||
const char *zFnctl;
|
||||
} aF[] = {
|
||||
{ SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, "BEGIN_ATOMIC_WRITE" },
|
||||
{ SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, "COMMIT_ATOMIC_WRITE" },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aF)/sizeof(aF[0]); i++){
|
||||
if( op==aF[i].iFnctl ) break;
|
||||
}
|
||||
if( i<sizeof(aF)/sizeof(aF[0]) ){
|
||||
int rc = 0;
|
||||
tvfsExecTcl(p, "xFileControl",
|
||||
Tcl_NewStringObj(pFd->zFilename, -1),
|
||||
Tcl_NewStringObj(aF[i].zFnctl, -1),
|
||||
0, 0
|
||||
);
|
||||
tvfsResultCode(p, &rc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}
|
||||
return sqlite3OsFileControl(pFd->pReal, op, pArg);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1160,6 +1185,7 @@ static int SQLITE_TCLAPI testvfs_obj_cmd(
|
||||
{ "xUnlock", TESTVFS_UNLOCK_MASK },
|
||||
{ "xLock", TESTVFS_LOCK_MASK },
|
||||
{ "xCheckReservedLock", TESTVFS_CKLOCK_MASK },
|
||||
{ "xFileControl", TESTVFS_FCNTL_MASK },
|
||||
};
|
||||
Tcl_Obj **apElem = 0;
|
||||
int nElem = 0;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
|
||||
#include "test_windirent.h"
|
||||
|
||||
/*
|
||||
|
||||
+40
-4
@@ -13,13 +13,17 @@
|
||||
** POSIX functions on Win32 using the MSVCRT.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
|
||||
#define SQLITE_WINDIRENT_H
|
||||
|
||||
/*
|
||||
** We need several data types from the Windows SDK header.
|
||||
*/
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
/*
|
||||
@@ -37,6 +41,33 @@
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/*
|
||||
** We may need several defines that should have been in "sys/stat.h".
|
||||
*/
|
||||
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(mode) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "mode_t" type.
|
||||
*/
|
||||
|
||||
#ifndef MODE_T_DEFINED
|
||||
#define MODE_T_DEFINED
|
||||
typedef unsigned short mode_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "ino_t" type.
|
||||
@@ -75,22 +106,27 @@
|
||||
** We need to provide the necessary structures and related types.
|
||||
*/
|
||||
|
||||
#ifndef DIRENT_DEFINED
|
||||
#define DIRENT_DEFINED
|
||||
typedef struct DIRENT DIRENT;
|
||||
typedef struct DIR DIR;
|
||||
typedef DIRENT *LPDIRENT;
|
||||
typedef DIR *LPDIR;
|
||||
|
||||
struct DIRENT {
|
||||
ino_t d_ino; /* Sequence number, do not use. */
|
||||
unsigned d_attributes; /* Win32 file attributes. */
|
||||
char d_name[NAME_MAX + 1]; /* Name within the directory. */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef DIR_DEFINED
|
||||
#define DIR_DEFINED
|
||||
typedef struct DIR DIR;
|
||||
typedef DIR *LPDIR;
|
||||
struct DIR {
|
||||
intptr_t d_handle; /* Value returned by "_findfirst". */
|
||||
DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
|
||||
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Provide a macro, for use by the implementation, to determine if a
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
** 2018 June 17
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include <tcl.h>
|
||||
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
typedef struct TestWindow TestWindow;
|
||||
struct TestWindow {
|
||||
Tcl_Obj *xStep;
|
||||
Tcl_Obj *xFinal;
|
||||
Tcl_Obj *xValue;
|
||||
Tcl_Obj *xInverse;
|
||||
Tcl_Interp *interp;
|
||||
};
|
||||
|
||||
typedef struct TestWindowCtx TestWindowCtx;
|
||||
struct TestWindowCtx {
|
||||
Tcl_Obj *pVal;
|
||||
};
|
||||
|
||||
static void doTestWindowStep(
|
||||
int bInverse,
|
||||
sqlite3_context *ctx,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
int i;
|
||||
TestWindow *p = (TestWindow*)sqlite3_user_data(ctx);
|
||||
Tcl_Obj *pEval = Tcl_DuplicateObj(bInverse ? p->xInverse : p->xStep);
|
||||
TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx));
|
||||
|
||||
Tcl_IncrRefCount(pEval);
|
||||
if( pCtx ){
|
||||
const char *zResult;
|
||||
int rc;
|
||||
if( pCtx->pVal ){
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal));
|
||||
}else{
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1));
|
||||
}
|
||||
for(i=0; i<nArg; i++){
|
||||
Tcl_Obj *pArg;
|
||||
pArg = Tcl_NewStringObj((const char*)sqlite3_value_text(apArg[i]), -1);
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, pArg);
|
||||
}
|
||||
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ){
|
||||
zResult = Tcl_GetStringResult(p->interp);
|
||||
sqlite3_result_error(ctx, zResult, -1);
|
||||
}else{
|
||||
if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal);
|
||||
pCtx->pVal = Tcl_DuplicateObj(Tcl_GetObjResult(p->interp));
|
||||
Tcl_IncrRefCount(pCtx->pVal);
|
||||
}
|
||||
}
|
||||
Tcl_DecrRefCount(pEval);
|
||||
}
|
||||
|
||||
static void doTestWindowFinalize(int bValue, sqlite3_context *ctx){
|
||||
TestWindow *p = (TestWindow*)sqlite3_user_data(ctx);
|
||||
Tcl_Obj *pEval = Tcl_DuplicateObj(bValue ? p->xValue : p->xFinal);
|
||||
TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx));
|
||||
|
||||
Tcl_IncrRefCount(pEval);
|
||||
if( pCtx ){
|
||||
const char *zResult;
|
||||
int rc;
|
||||
if( pCtx->pVal ){
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal));
|
||||
}else{
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1));
|
||||
}
|
||||
|
||||
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
zResult = Tcl_GetStringResult(p->interp);
|
||||
if( rc!=TCL_OK ){
|
||||
sqlite3_result_error(ctx, zResult, -1);
|
||||
}else{
|
||||
sqlite3_result_text(ctx, zResult, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
if( bValue==0 ){
|
||||
if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal);
|
||||
pCtx->pVal = 0;
|
||||
}
|
||||
}
|
||||
Tcl_DecrRefCount(pEval);
|
||||
}
|
||||
|
||||
static void testWindowStep(
|
||||
sqlite3_context *ctx,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
doTestWindowStep(0, ctx, nArg, apArg);
|
||||
}
|
||||
static void testWindowInverse(
|
||||
sqlite3_context *ctx,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
doTestWindowStep(1, ctx, nArg, apArg);
|
||||
}
|
||||
|
||||
static void testWindowFinal(sqlite3_context *ctx){
|
||||
doTestWindowFinalize(0, ctx);
|
||||
}
|
||||
static void testWindowValue(sqlite3_context *ctx){
|
||||
doTestWindowFinalize(1, ctx);
|
||||
}
|
||||
|
||||
static void testWindowDestroy(void *pCtx){
|
||||
ckfree(pCtx);
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_create_window_function DB NAME XSTEP XFINAL XVALUE XINVERSE
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_create_window(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
TestWindow *pNew;
|
||||
sqlite3 *db;
|
||||
const char *zName;
|
||||
int rc;
|
||||
|
||||
if( objc!=7 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME XSTEP XFINAL XVALUE XINVERSE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zName = Tcl_GetString(objv[2]);
|
||||
pNew = (TestWindow*)ckalloc(sizeof(TestWindow));
|
||||
memset(pNew, 0, sizeof(TestWindow));
|
||||
pNew->xStep = Tcl_DuplicateObj(objv[3]);
|
||||
pNew->xFinal = Tcl_DuplicateObj(objv[4]);
|
||||
pNew->xValue = Tcl_DuplicateObj(objv[5]);
|
||||
pNew->xInverse = Tcl_DuplicateObj(objv[6]);
|
||||
pNew->interp = interp;
|
||||
|
||||
Tcl_IncrRefCount(pNew->xStep);
|
||||
Tcl_IncrRefCount(pNew->xFinal);
|
||||
Tcl_IncrRefCount(pNew->xValue);
|
||||
Tcl_IncrRefCount(pNew->xInverse);
|
||||
|
||||
rc = sqlite3_create_window_function(db, zName, -1, SQLITE_UTF8, (void*)pNew,
|
||||
testWindowStep, testWindowFinal, testWindowValue, testWindowInverse,
|
||||
testWindowDestroy
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI test_create_window_misuse(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0,
|
||||
0, testWindowFinal, testWindowValue, testWindowInverse,
|
||||
0
|
||||
);
|
||||
if( rc!=SQLITE_MISUSE ) goto error;
|
||||
rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0,
|
||||
testWindowStep, 0, testWindowValue, testWindowInverse,
|
||||
0
|
||||
);
|
||||
if( rc!=SQLITE_MISUSE ) goto error;
|
||||
rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0,
|
||||
testWindowStep, testWindowFinal, 0, testWindowInverse,
|
||||
0
|
||||
);
|
||||
if( rc!=SQLITE_MISUSE ) goto error;
|
||||
rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0,
|
||||
testWindowStep, testWindowFinal, testWindowValue, 0,
|
||||
0
|
||||
);
|
||||
if( rc!=SQLITE_MISUSE ) goto error;
|
||||
|
||||
return TCL_OK;
|
||||
|
||||
error:
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj("misuse test error", -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** xStep for sumint().
|
||||
*/
|
||||
static void sumintStep(
|
||||
sqlite3_context *ctx,
|
||||
int nArg,
|
||||
sqlite3_value *apArg[]
|
||||
){
|
||||
sqlite3_int64 *pInt;
|
||||
|
||||
assert( nArg==1 );
|
||||
if( sqlite3_value_type(apArg[0])!=SQLITE_INTEGER ){
|
||||
sqlite3_result_error(ctx, "invalid argument", -1);
|
||||
return;
|
||||
}
|
||||
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64));
|
||||
if( pInt ){
|
||||
*pInt += sqlite3_value_int64(apArg[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xInverse for sumint().
|
||||
*/
|
||||
static void sumintInverse(
|
||||
sqlite3_context *ctx,
|
||||
int nArg,
|
||||
sqlite3_value *apArg[]
|
||||
){
|
||||
sqlite3_int64 *pInt;
|
||||
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64));
|
||||
*pInt -= sqlite3_value_int64(apArg[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
** xFinal for sumint().
|
||||
*/
|
||||
static void sumintFinal(sqlite3_context *ctx){
|
||||
sqlite3_int64 res = 0;
|
||||
sqlite3_int64 *pInt;
|
||||
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0);
|
||||
if( pInt ) res = *pInt;
|
||||
sqlite3_result_int64(ctx, res);
|
||||
}
|
||||
|
||||
/*
|
||||
** xValue for sumint().
|
||||
*/
|
||||
static void sumintValue(sqlite3_context *ctx){
|
||||
sqlite3_int64 res = 0;
|
||||
sqlite3_int64 *pInt;
|
||||
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0);
|
||||
if( pInt ) res = *pInt;
|
||||
sqlite3_result_int64(ctx, res);
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI test_create_sumint(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_create_window_function(db, "sumint", 1, SQLITE_UTF8, 0,
|
||||
sumintStep, sumintFinal, sumintValue, sumintInverse,
|
||||
0
|
||||
);
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI test_override_sum(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_create_function(db, "sum", -1, SQLITE_UTF8, 0,
|
||||
0, sumintStep, sumintFinal
|
||||
);
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetest_window_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
int clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "sqlite3_create_window_function", test_create_window, 0 },
|
||||
{ "test_create_window_function_misuse", test_create_window_misuse, 0 },
|
||||
{ "test_create_sumint", test_create_sumint, 0 },
|
||||
{ "test_override_sum", test_override_sum, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData);
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
+137
-37
@@ -54,11 +54,12 @@
|
||||
#define CC_TILDA 25 /* '~' */
|
||||
#define CC_DOT 26 /* '.' */
|
||||
#define CC_ILLEGAL 27 /* Illegal character */
|
||||
#define CC_NUL 28 /* 0x00 */
|
||||
|
||||
static const unsigned char aiClass[] = {
|
||||
#ifdef SQLITE_ASCII
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
|
||||
/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
|
||||
/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
|
||||
/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
|
||||
/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
|
||||
@@ -183,11 +184,85 @@ const char sqlite3IsEbcdicIdChar[] = {
|
||||
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
|
||||
#endif
|
||||
|
||||
/* Make the IdChar function accessible from ctime.c */
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
/* Make the IdChar function accessible from ctime.c and alter.c */
|
||||
int sqlite3IsIdChar(u8 c){ return IdChar(c); }
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
/*
|
||||
** Return the id of the next token in string (*pz). Before returning, set
|
||||
** (*pz) to point to the byte following the parsed token.
|
||||
*/
|
||||
static int getToken(const unsigned char **pz){
|
||||
const unsigned char *z = *pz;
|
||||
int t; /* Token type to return */
|
||||
do {
|
||||
z += sqlite3GetToken(z, &t);
|
||||
}while( t==TK_SPACE );
|
||||
if( t==TK_ID
|
||||
|| t==TK_STRING
|
||||
|| t==TK_JOIN_KW
|
||||
|| t==TK_WINDOW
|
||||
|| t==TK_OVER
|
||||
|| sqlite3ParserFallback(t)==TK_ID
|
||||
){
|
||||
t = TK_ID;
|
||||
}
|
||||
*pz = z;
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following three functions are called immediately after the tokenizer
|
||||
** reads the keywords WINDOW, OVER and FILTER, respectively, to determine
|
||||
** whether the token should be treated as a keyword or an SQL identifier.
|
||||
** This cannot be handled by the usual lemon %fallback method, due to
|
||||
** the ambiguity in some constructions. e.g.
|
||||
**
|
||||
** SELECT sum(x) OVER ...
|
||||
**
|
||||
** In the above, "OVER" might be a keyword, or it might be an alias for the
|
||||
** sum(x) expression. If a "%fallback ID OVER" directive were added to
|
||||
** grammar, then SQLite would always treat "OVER" as an alias, making it
|
||||
** impossible to call a window-function without a FILTER clause.
|
||||
**
|
||||
** WINDOW is treated as a keyword if:
|
||||
**
|
||||
** * the following token is an identifier, or a keyword that can fallback
|
||||
** to being an identifier, and
|
||||
** * the token after than one is TK_AS.
|
||||
**
|
||||
** OVER is a keyword if:
|
||||
**
|
||||
** * the previous token was TK_RP, and
|
||||
** * the next token is either TK_LP or an identifier.
|
||||
**
|
||||
** FILTER is a keyword if:
|
||||
**
|
||||
** * the previous token was TK_RP, and
|
||||
** * the next token is TK_LP.
|
||||
*/
|
||||
static int analyzeWindowKeyword(const unsigned char *z){
|
||||
int t;
|
||||
t = getToken(&z);
|
||||
if( t!=TK_ID ) return TK_ID;
|
||||
t = getToken(&z);
|
||||
if( t!=TK_AS ) return TK_ID;
|
||||
return TK_WINDOW;
|
||||
}
|
||||
static int analyzeOverKeyword(const unsigned char *z, int lastToken){
|
||||
if( lastToken==TK_RP ){
|
||||
int t = getToken(&z);
|
||||
if( t==TK_LP || t==TK_ID ) return TK_OVER;
|
||||
}
|
||||
return TK_ID;
|
||||
}
|
||||
static int analyzeFilterKeyword(const unsigned char *z, int lastToken){
|
||||
if( lastToken==TK_RP && getToken(&z)==TK_LP ){
|
||||
return TK_FILTER;
|
||||
}
|
||||
return TK_ID;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** Return the length (in bytes) of the token that begins at z[0].
|
||||
@@ -456,6 +531,10 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
i = 1;
|
||||
break;
|
||||
}
|
||||
case CC_NUL: {
|
||||
*tokenType = TK_ILLEGAL;
|
||||
return 0;
|
||||
}
|
||||
default: {
|
||||
*tokenType = TK_ILLEGAL;
|
||||
return 1;
|
||||
@@ -496,9 +575,9 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
/* sqlite3ParserTrace(stdout, "parser: "); */
|
||||
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
||||
pEngine = &sEngine;
|
||||
sqlite3ParserInit(pEngine);
|
||||
sqlite3ParserInit(pEngine, pParse);
|
||||
#else
|
||||
pEngine = sqlite3ParserAlloc(sqlite3Malloc);
|
||||
pEngine = sqlite3ParserAlloc(sqlite3Malloc, pParse);
|
||||
if( pEngine==0 ){
|
||||
sqlite3OomFault(db);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
@@ -509,47 +588,64 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pParse->nVar==0 );
|
||||
assert( pParse->pVList==0 );
|
||||
while( 1 ){
|
||||
if( zSql[0]!=0 ){
|
||||
n = sqlite3GetToken((u8*)zSql, &tokenType);
|
||||
mxSqlLen -= n;
|
||||
if( mxSqlLen<0 ){
|
||||
pParse->rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
/* Upon reaching the end of input, call the parser two more times
|
||||
** with tokens TK_SEMI and 0, in that order. */
|
||||
if( lastTokenParsed==TK_SEMI ){
|
||||
tokenType = 0;
|
||||
}else if( lastTokenParsed==0 ){
|
||||
break;
|
||||
}else{
|
||||
tokenType = TK_SEMI;
|
||||
}
|
||||
zSql -= n;
|
||||
n = sqlite3GetToken((u8*)zSql, &tokenType);
|
||||
mxSqlLen -= n;
|
||||
if( mxSqlLen<0 ){
|
||||
pParse->rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( tokenType>=TK_WINDOW ){
|
||||
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|
||||
|| tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
|
||||
);
|
||||
#else
|
||||
if( tokenType>=TK_SPACE ){
|
||||
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
if( db->u1.isInterrupted ){
|
||||
pParse->rc = SQLITE_INTERRUPT;
|
||||
break;
|
||||
}
|
||||
if( tokenType==TK_ILLEGAL ){
|
||||
if( tokenType==TK_SPACE ){
|
||||
zSql += n;
|
||||
continue;
|
||||
}
|
||||
if( zSql[0]==0 ){
|
||||
/* Upon reaching the end of input, call the parser two more times
|
||||
** with tokens TK_SEMI and 0, in that order. */
|
||||
if( lastTokenParsed==TK_SEMI ){
|
||||
tokenType = 0;
|
||||
}else if( lastTokenParsed==0 ){
|
||||
break;
|
||||
}else{
|
||||
tokenType = TK_SEMI;
|
||||
}
|
||||
n = 0;
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
}else if( tokenType==TK_WINDOW ){
|
||||
assert( n==6 );
|
||||
tokenType = analyzeWindowKeyword((const u8*)&zSql[6]);
|
||||
}else if( tokenType==TK_OVER ){
|
||||
assert( n==4 );
|
||||
tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
|
||||
}else if( tokenType==TK_FILTER ){
|
||||
assert( n==6 );
|
||||
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql);
|
||||
break;
|
||||
}
|
||||
zSql += n;
|
||||
}else{
|
||||
pParse->sLastToken.z = zSql;
|
||||
pParse->sLastToken.n = n;
|
||||
sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
|
||||
lastTokenParsed = tokenType;
|
||||
zSql += n;
|
||||
if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
|
||||
}
|
||||
pParse->sLastToken.z = zSql;
|
||||
pParse->sLastToken.n = n;
|
||||
sqlite3Parser(pEngine, tokenType, pParse->sLastToken);
|
||||
lastTokenParsed = tokenType;
|
||||
zSql += n;
|
||||
if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
|
||||
}
|
||||
assert( nErr==0 );
|
||||
pParse->zTail = zSql;
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
sqlite3_mutex_enter(sqlite3MallocMutex());
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
|
||||
@@ -571,10 +667,12 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pzErrMsg!=0 );
|
||||
if( pParse->zErrMsg ){
|
||||
*pzErrMsg = pParse->zErrMsg;
|
||||
sqlite3_log(pParse->rc, "%s", *pzErrMsg);
|
||||
sqlite3_log(pParse->rc, "%s in \"%s\"",
|
||||
*pzErrMsg, pParse->zTail);
|
||||
pParse->zErrMsg = 0;
|
||||
nErr++;
|
||||
}
|
||||
pParse->zTail = zSql;
|
||||
if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){
|
||||
sqlite3VdbeDelete(pParse->pVdbe);
|
||||
pParse->pVdbe = 0;
|
||||
@@ -590,16 +688,18 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
sqlite3_free(pParse->apVtabLock);
|
||||
#endif
|
||||
|
||||
if( !IN_DECLARE_VTAB ){
|
||||
if( !IN_SPECIAL_PARSE ){
|
||||
/* If the pParse->declareVtab flag is set, do not delete any table
|
||||
** structure built up in pParse->pNewTable. The calling code (see vtab.c)
|
||||
** will take responsibility for freeing the Table structure.
|
||||
*/
|
||||
sqlite3DeleteTable(db, pParse->pNewTable);
|
||||
}
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
}
|
||||
|
||||
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
sqlite3DbFree(db, pParse->pVList);
|
||||
while( pParse->pAinc ){
|
||||
AutoincInfo *p = pParse->pAinc;
|
||||
|
||||
+183
-32
@@ -58,15 +58,17 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
if( p ){
|
||||
for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
|
||||
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
|
||||
sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
|
||||
}
|
||||
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
||||
sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
||||
}
|
||||
if( zFormat!=0 ){
|
||||
va_start(ap, zFormat);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
va_end(ap);
|
||||
assert( acc.nChar>0 );
|
||||
sqlite3_str_append(&acc, "\n", 1);
|
||||
}
|
||||
va_start(ap, zFormat);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
va_end(ap);
|
||||
assert( acc.nChar>0 );
|
||||
if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
fflush(stdout);
|
||||
@@ -99,17 +101,17 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
|
||||
char zLine[1000];
|
||||
const struct Cte *pCte = &pWith->a[i];
|
||||
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
||||
sqlite3XPrintf(&x, "%s", pCte->zName);
|
||||
sqlite3_str_appendf(&x, "%s", pCte->zName);
|
||||
if( pCte->pCols && pCte->pCols->nExpr>0 ){
|
||||
char cSep = '(';
|
||||
int j;
|
||||
for(j=0; j<pCte->pCols->nExpr; j++){
|
||||
sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
||||
sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
||||
cSep = ',';
|
||||
}
|
||||
sqlite3XPrintf(&x, ")");
|
||||
sqlite3_str_appendf(&x, ")");
|
||||
}
|
||||
sqlite3XPrintf(&x, " AS");
|
||||
sqlite3_str_appendf(&x, " AS");
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
|
||||
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
|
||||
@@ -137,9 +139,11 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
sqlite3TreeViewPush(pView, 1);
|
||||
}
|
||||
do{
|
||||
sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
|
||||
sqlite3TreeViewLine(pView,
|
||||
"SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
|
||||
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
||||
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
|
||||
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
|
||||
p->selId, p, p->selFlags,
|
||||
(int)p->nSelectRow
|
||||
);
|
||||
if( cnt++ ) sqlite3TreeViewPop(pView);
|
||||
@@ -153,9 +157,23 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
if( p->pHaving ) n++;
|
||||
if( p->pOrderBy ) n++;
|
||||
if( p->pLimit ) n++;
|
||||
if( p->pOffset ) n++;
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( p->pWin ) n++;
|
||||
if( p->pWinDefn ) n++;
|
||||
#endif
|
||||
}
|
||||
sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( p->pWin ){
|
||||
Window *pX;
|
||||
pView = sqlite3TreeViewPush(pView, (n--)>0);
|
||||
sqlite3TreeViewLine(pView, "window-functions");
|
||||
for(pX=p->pWin; pX; pX=pX->pNextWin){
|
||||
sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0);
|
||||
}
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
#endif
|
||||
if( p->pSrc && p->pSrc->nSrc ){
|
||||
int i;
|
||||
pView = sqlite3TreeViewPush(pView, (n--)>0);
|
||||
@@ -165,20 +183,20 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
StrAccum x;
|
||||
char zLine[100];
|
||||
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
||||
sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
|
||||
sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor);
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
||||
sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
||||
}else if( pItem->zName ){
|
||||
sqlite3XPrintf(&x, " %s", pItem->zName);
|
||||
sqlite3_str_appendf(&x, " %s", pItem->zName);
|
||||
}
|
||||
if( pItem->pTab ){
|
||||
sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
|
||||
sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
|
||||
}
|
||||
if( pItem->zAlias ){
|
||||
sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
|
||||
sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
|
||||
}
|
||||
if( pItem->fg.jointype & JT_LEFT ){
|
||||
sqlite3XPrintf(&x, " LEFT-JOIN");
|
||||
sqlite3_str_appendf(&x, " LEFT-JOIN");
|
||||
}
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
|
||||
@@ -205,17 +223,27 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
sqlite3TreeViewExpr(pView, p->pHaving, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( p->pWinDefn ){
|
||||
Window *pX;
|
||||
sqlite3TreeViewItem(pView, "WINDOW", (n--)>0);
|
||||
for(pX=p->pWinDefn; pX; pX=pX->pNextWin){
|
||||
sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0);
|
||||
}
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
#endif
|
||||
if( p->pOrderBy ){
|
||||
sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
|
||||
}
|
||||
if( p->pLimit ){
|
||||
sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
|
||||
sqlite3TreeViewExpr(pView, p->pLimit, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
if( p->pOffset ){
|
||||
sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
||||
sqlite3TreeViewExpr(pView, p->pOffset, 0);
|
||||
sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0);
|
||||
if( p->pLimit->pRight ){
|
||||
sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
||||
sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
if( p->pPrior ){
|
||||
@@ -232,6 +260,83 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
/*
|
||||
** Generate a description of starting or stopping bounds
|
||||
*/
|
||||
void sqlite3TreeViewBound(
|
||||
TreeView *pView, /* View context */
|
||||
u8 eBound, /* UNBOUNDED, CURRENT, PRECEDING, FOLLOWING */
|
||||
Expr *pExpr, /* Value for PRECEDING or FOLLOWING */
|
||||
u8 moreToFollow /* True if more to follow */
|
||||
){
|
||||
switch( eBound ){
|
||||
case TK_UNBOUNDED: {
|
||||
sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow);
|
||||
sqlite3TreeViewPop(pView);
|
||||
break;
|
||||
}
|
||||
case TK_CURRENT: {
|
||||
sqlite3TreeViewItem(pView, "CURRENT", moreToFollow);
|
||||
sqlite3TreeViewPop(pView);
|
||||
break;
|
||||
}
|
||||
case TK_PRECEDING: {
|
||||
sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow);
|
||||
sqlite3TreeViewExpr(pView, pExpr, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
break;
|
||||
}
|
||||
case TK_FOLLOWING: {
|
||||
sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow);
|
||||
sqlite3TreeViewExpr(pView, pExpr, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
/*
|
||||
** Generate a human-readable explanation for a Window object
|
||||
*/
|
||||
void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
|
||||
pView = sqlite3TreeViewPush(pView, more);
|
||||
if( pWin->zName ){
|
||||
sqlite3TreeViewLine(pView, "OVER %s", pWin->zName);
|
||||
}else{
|
||||
sqlite3TreeViewLine(pView, "OVER");
|
||||
}
|
||||
if( pWin->pPartition ){
|
||||
sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY");
|
||||
}
|
||||
if( pWin->pOrderBy ){
|
||||
sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY");
|
||||
}
|
||||
if( pWin->eType ){
|
||||
sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0);
|
||||
sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1);
|
||||
sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
/*
|
||||
** Generate a human-readable explanation for a Window Function object
|
||||
*/
|
||||
void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){
|
||||
pView = sqlite3TreeViewPush(pView, more);
|
||||
sqlite3TreeViewLine(pView, "WINFUNC %s(%d)",
|
||||
pWin->pFunc->zName, pWin->pFunc->nArg);
|
||||
sqlite3TreeViewWindow(pView, pWin, 0);
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression tree.
|
||||
*/
|
||||
@@ -269,6 +374,9 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewLine(pView, "{%d:%d}%s",
|
||||
pExpr->iTable, pExpr->iColumn, zFlgs);
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_FixedCol) ){
|
||||
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_INTEGER: {
|
||||
@@ -293,6 +401,11 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewLine(pView,"NULL");
|
||||
break;
|
||||
}
|
||||
case TK_TRUEFALSE: {
|
||||
sqlite3TreeViewLine(pView,
|
||||
sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE");
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
||||
case TK_BLOB: {
|
||||
sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
|
||||
@@ -349,6 +462,19 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
case TK_ISNULL: zUniOp = "ISNULL"; break;
|
||||
case TK_NOTNULL: zUniOp = "NOTNULL"; break;
|
||||
|
||||
case TK_TRUTH: {
|
||||
int x;
|
||||
const char *azOp[] = {
|
||||
"IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE"
|
||||
};
|
||||
assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT );
|
||||
assert( pExpr->pRight );
|
||||
assert( pExpr->pRight->op==TK_TRUEFALSE );
|
||||
x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight);
|
||||
zUniOp = azOp[x];
|
||||
break;
|
||||
}
|
||||
|
||||
case TK_SPAN: {
|
||||
sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
|
||||
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
||||
@@ -364,10 +490,17 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pFarg; /* List of function arguments */
|
||||
Window *pWin;
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly) ){
|
||||
pFarg = 0;
|
||||
pWin = 0;
|
||||
}else{
|
||||
pFarg = pExpr->x.pList;
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
pWin = pExpr->pWin;
|
||||
#else
|
||||
pWin = 0;
|
||||
#endif
|
||||
}
|
||||
if( pExpr->op==TK_AGG_FUNCTION ){
|
||||
sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
|
||||
@@ -376,8 +509,13 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
|
||||
}
|
||||
if( pFarg ){
|
||||
sqlite3TreeViewExprList(pView, pFarg, 0, 0);
|
||||
sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pWin ){
|
||||
sqlite3TreeViewWindow(pView, pWin, 0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
@@ -508,12 +646,25 @@ void sqlite3TreeViewBareExprList(
|
||||
sqlite3TreeViewLine(pView, "%s", zLabel);
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
int j = pList->a[i].u.x.iOrderByCol;
|
||||
if( j ){
|
||||
sqlite3TreeViewPush(pView, 0);
|
||||
sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
|
||||
char *zName = pList->a[i].zName;
|
||||
int moreToFollow = i<pList->nExpr - 1;
|
||||
if( j || zName ){
|
||||
sqlite3TreeViewPush(pView, moreToFollow);
|
||||
moreToFollow = 0;
|
||||
sqlite3TreeViewLine(pView, 0);
|
||||
if( zName ){
|
||||
fprintf(stdout, "AS %s ", zName);
|
||||
}
|
||||
if( j ){
|
||||
fprintf(stdout, "iOrderByCol=%d", j);
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow);
|
||||
if( j || zName ){
|
||||
sqlite3TreeViewPop(pView);
|
||||
}
|
||||
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
|
||||
if( j ) sqlite3TreeViewPop(pView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-35
@@ -25,6 +25,8 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
|
||||
sqlite3ExprListDelete(db, pTmp->pExprList);
|
||||
sqlite3SelectDelete(db, pTmp->pSelect);
|
||||
sqlite3IdListDelete(db, pTmp->pIdList);
|
||||
sqlite3UpsertDelete(db, pTmp->pUpsert);
|
||||
sqlite3DbFree(db, pTmp->zSpan);
|
||||
|
||||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
@@ -179,14 +181,16 @@ void sqlite3BeginTrigger(
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
|
||||
if( !noErr ){
|
||||
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
|
||||
}else{
|
||||
assert( !db->init.busy );
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
|
||||
if( !noErr ){
|
||||
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
|
||||
}else{
|
||||
assert( !db->init.busy );
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
}
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
|
||||
/* Do not create a trigger on a system table */
|
||||
@@ -210,7 +214,7 @@ void sqlite3BeginTrigger(
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
int code = SQLITE_CREATE_TRIGGER;
|
||||
const char *zDb = db->aDb[iTabDb].zDbSName;
|
||||
@@ -244,8 +248,15 @@ void sqlite3BeginTrigger(
|
||||
pTrigger->pTabSchema = pTab->pSchema;
|
||||
pTrigger->op = (u8)op;
|
||||
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
|
||||
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
||||
pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
|
||||
pTrigger->pWhen = pWhen;
|
||||
pWhen = 0;
|
||||
}else{
|
||||
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
||||
}
|
||||
pTrigger->pColumns = pColumns;
|
||||
pColumns = 0;
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
pParse->pNewTrigger = pTrigger;
|
||||
|
||||
@@ -294,6 +305,14 @@ void sqlite3FinishTrigger(
|
||||
goto triggerfinish_cleanup;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
if( IN_RENAME_OBJECT ){
|
||||
assert( !db->init.busy );
|
||||
pParse->pNewTrigger = pTrig;
|
||||
pTrig = 0;
|
||||
}else
|
||||
#endif
|
||||
|
||||
/* if we are not initializing,
|
||||
** build the sqlite_master entry
|
||||
*/
|
||||
@@ -335,10 +354,21 @@ void sqlite3FinishTrigger(
|
||||
|
||||
triggerfinish_cleanup:
|
||||
sqlite3DeleteTrigger(db, pTrig);
|
||||
assert( !pParse->pNewTrigger );
|
||||
assert( IN_RENAME_OBJECT || !pParse->pNewTrigger );
|
||||
sqlite3DeleteTriggerStep(db, pStepList);
|
||||
}
|
||||
|
||||
/*
|
||||
** Duplicate a range of text from an SQL statement, then convert all
|
||||
** whitespace characters into ordinary space characters.
|
||||
*/
|
||||
static char *triggerSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
||||
char *z = sqlite3DbSpanDup(db, zStart, zEnd);
|
||||
int i;
|
||||
if( z ) for(i=0; z[i]; i++) if( sqlite3Isspace(z[i]) ) z[i] = ' ';
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Turn a SELECT statement (that the pSelect parameter points to) into
|
||||
** a trigger step. Return a pointer to a TriggerStep structure.
|
||||
@@ -346,7 +376,12 @@ triggerfinish_cleanup:
|
||||
** The parser calls this routine when it finds a SELECT statement in
|
||||
** body of a TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
TriggerStep *sqlite3TriggerSelectStep(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Select *pSelect, /* The SELECT statement */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) {
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
@@ -355,6 +390,7 @@ TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
pTriggerStep->op = TK_SELECT;
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
@@ -365,10 +401,13 @@ TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
** If an OOM error occurs, NULL is returned and db->mallocFailed is set.
|
||||
*/
|
||||
static TriggerStep *triggerStepAllocate(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Parse *pParse, /* Parser context */
|
||||
u8 op, /* Trigger opcode */
|
||||
Token *pName /* The target name */
|
||||
Token *pName, /* The target name */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
|
||||
@@ -378,6 +417,10 @@ static TriggerStep *triggerStepAllocate(
|
||||
sqlite3Dequote(z);
|
||||
pTriggerStep->zTarget = z;
|
||||
pTriggerStep->op = op;
|
||||
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
|
||||
}
|
||||
}
|
||||
return pTriggerStep;
|
||||
}
|
||||
@@ -390,23 +433,36 @@ static TriggerStep *triggerStepAllocate(
|
||||
** body of a trigger.
|
||||
*/
|
||||
TriggerStep *sqlite3TriggerInsertStep(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Parse *pParse, /* Parser */
|
||||
Token *pTableName, /* Name of the table into which we insert */
|
||||
IdList *pColumn, /* List of columns in pTableName to insert into */
|
||||
Select *pSelect, /* A SELECT statement that supplies values */
|
||||
u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
Upsert *pUpsert, /* ON CONFLICT clauses for upsert */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
assert(pSelect != 0 || db->mallocFailed);
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pSelect = 0;
|
||||
}else{
|
||||
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
}
|
||||
pTriggerStep->pIdList = pColumn;
|
||||
pTriggerStep->pUpsert = pUpsert;
|
||||
pTriggerStep->orconf = orconf;
|
||||
}else{
|
||||
testcase( pColumn );
|
||||
sqlite3IdListDelete(db, pColumn);
|
||||
testcase( pUpsert );
|
||||
sqlite3UpsertDelete(db, pUpsert);
|
||||
}
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
|
||||
@@ -419,18 +475,28 @@ TriggerStep *sqlite3TriggerInsertStep(
|
||||
** sees an UPDATE statement inside the body of a CREATE TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqlite3TriggerUpdateStep(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Parse *pParse, /* Parser */
|
||||
Token *pTableName, /* Name of the table to be updated */
|
||||
ExprList *pEList, /* The SET clause: list of column and new values */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pExprList = pEList;
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pEList = 0;
|
||||
pWhere = 0;
|
||||
}else{
|
||||
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
}
|
||||
pTriggerStep->orconf = orconf;
|
||||
}
|
||||
sqlite3ExprListDelete(db, pEList);
|
||||
@@ -444,15 +510,23 @@ TriggerStep *sqlite3TriggerUpdateStep(
|
||||
** sees a DELETE statement inside the body of a CREATE TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqlite3TriggerDeleteStep(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Parse *pParse, /* Parser */
|
||||
Token *pTableName, /* The table from which rows are deleted */
|
||||
Expr *pWhere /* The WHERE clause */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pWhere = 0;
|
||||
}else{
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
}
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
}
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
@@ -585,7 +659,7 @@ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
|
||||
*pp = (*pp)->pNext;
|
||||
}
|
||||
sqlite3DeleteTrigger(db, pTrigger);
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,13 +779,21 @@ static int codeTriggerProgram(
|
||||
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
|
||||
assert( pParse->okConstFactor==0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( pStep->zSpan ){
|
||||
sqlite3VdbeAddOp4(v, OP_Trace, 0x7fffffff, 1, 0,
|
||||
sqlite3MPrintf(db, "-- %s", pStep->zSpan),
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch( pStep->op ){
|
||||
case TK_UPDATE: {
|
||||
sqlite3Update(pParse,
|
||||
targetSrcList(pParse, pStep),
|
||||
sqlite3ExprListDup(db, pStep->pExprList, 0),
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0),
|
||||
pParse->eOrconf
|
||||
pParse->eOrconf, 0, 0, 0
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -720,14 +802,15 @@ static int codeTriggerProgram(
|
||||
targetSrcList(pParse, pStep),
|
||||
sqlite3SelectDup(db, pStep->pSelect, 0),
|
||||
sqlite3IdListDup(db, pStep->pIdList),
|
||||
pParse->eOrconf
|
||||
pParse->eOrconf,
|
||||
sqlite3UpsertDup(db, pStep->pUpsert)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TK_DELETE: {
|
||||
sqlite3DeleteFrom(pParse,
|
||||
targetSrcList(pParse, pStep),
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0)
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -845,9 +928,11 @@ static TriggerPrg *codeRowTrigger(
|
||||
pTab->zName
|
||||
));
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
sqlite3VdbeChangeP4(v, -1,
|
||||
sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
||||
);
|
||||
if( pTrigger->zName ){
|
||||
sqlite3VdbeChangeP4(v, -1,
|
||||
sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If one was specified, code the WHEN clause. If it evaluates to false
|
||||
@@ -875,7 +960,7 @@ static TriggerPrg *codeRowTrigger(
|
||||
VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf)));
|
||||
|
||||
transferParseError(pParse, pSubParse);
|
||||
if( db->mallocFailed==0 ){
|
||||
if( db->mallocFailed==0 && pParse->nErr==0 ){
|
||||
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
|
||||
}
|
||||
pProgram->nMem = pSubParse->nMem;
|
||||
|
||||
+198
-125
@@ -91,7 +91,10 @@ void sqlite3Update(
|
||||
SrcList *pTabList, /* The table in which we should change things */
|
||||
ExprList *pChanges, /* Things to be changed */
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
int onError /* How to handle constraint errors */
|
||||
int onError, /* How to handle constraint errors */
|
||||
ExprList *pOrderBy, /* ORDER BY clause. May be null */
|
||||
Expr *pLimit, /* LIMIT clause. May be null */
|
||||
Upsert *pUpsert /* ON CONFLICT clause, or null */
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
Table *pTab; /* The table to be updated */
|
||||
@@ -176,6 +179,16 @@ void sqlite3Update(
|
||||
# define isView 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
if( !isView ){
|
||||
pWhere = sqlite3LimitWhere(
|
||||
pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE"
|
||||
);
|
||||
pOrderBy = 0;
|
||||
pLimit = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
@@ -188,16 +201,23 @@ void sqlite3Update(
|
||||
** need to occur right after the database cursor. So go ahead and
|
||||
** allocate enough space, just in case.
|
||||
*/
|
||||
pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
|
||||
iBaseCur = iDataCur = pParse->nTab++;
|
||||
iIdxCur = iDataCur+1;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
testcase( pPk!=0 && pPk!=pTab->pIndex );
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
||||
if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
|
||||
if( pPk==pIdx ){
|
||||
iDataCur = pParse->nTab;
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
}
|
||||
pParse->nTab++;
|
||||
}
|
||||
if( pUpsert ){
|
||||
/* On an UPSERT, reuse the same cursors already opened by INSERT */
|
||||
iDataCur = pUpsert->iDataCur;
|
||||
iIdxCur = pUpsert->iIdxCur;
|
||||
pParse->nTab = iBaseCur;
|
||||
}
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
|
||||
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
|
||||
** Initialize aXRef[] and aToOpen[] to their default values.
|
||||
@@ -214,6 +234,8 @@ void sqlite3Update(
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
sNC.uNC.pUpsert = pUpsert;
|
||||
sNC.ncFlags = NC_UUpsert;
|
||||
|
||||
/* Resolve the column names in all the expressions of the
|
||||
** of the UPDATE statement. Also find the column index
|
||||
@@ -317,7 +339,7 @@ void sqlite3Update(
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto update_cleanup;
|
||||
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3BeginWriteOperation(pParse, pTrigger || hasFK, iDb);
|
||||
|
||||
/* Allocate required registers. */
|
||||
if( !IsVirtual(pTab) ){
|
||||
@@ -344,7 +366,11 @@ void sqlite3Update(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
|
||||
sqlite3MaterializeView(pParse, pTab,
|
||||
pWhere, pOrderBy, pLimit, iDataCur
|
||||
);
|
||||
pOrderBy = 0;
|
||||
pLimit = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -364,8 +390,16 @@ void sqlite3Update(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize the count of updated rows */
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
/* Jump to labelBreak to abandon further processing of this UPDATE */
|
||||
labelContinue = labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
/* Not an UPSERT. Normal processing. Begin by
|
||||
** initialize the count of updated rows */
|
||||
if( (db->flags&SQLITE_CountRows)!=0
|
||||
&& !pParse->pTriggerTab
|
||||
&& !pParse->nested
|
||||
&& pUpsert==0
|
||||
){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
@@ -378,46 +412,61 @@ void sqlite3Update(
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
regKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
}
|
||||
|
||||
/* Begin the database scan.
|
||||
**
|
||||
** Do not consider a single-pass strategy for a multi-row update if
|
||||
** there are any triggers or foreign keys to process, or rows may
|
||||
** be deleted as a result of REPLACE conflict handling. Any of these
|
||||
** things might disturb a cursor being used to scan through the table
|
||||
** or index, causing a single-pass approach to malfunction. */
|
||||
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* A one-pass strategy that might update more than one row may not
|
||||
** be used if any column of the index used for the scan is being
|
||||
** updated. Otherwise, if there is an index on "b", statements like
|
||||
** the following could create an infinite loop:
|
||||
**
|
||||
** UPDATE t1 SET b=b+1 WHERE b>?
|
||||
**
|
||||
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
||||
** strategy that uses an index for which one or more columns are being
|
||||
** updated. */
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
int iCur = aiCurOnePass[1];
|
||||
if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
||||
eOnePass = ONEPASS_OFF;
|
||||
if( pUpsert==0 ){
|
||||
iEph = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
}
|
||||
assert( iCur!=iDataCur || !HasRowid(pTab) );
|
||||
}
|
||||
|
||||
if( pUpsert ){
|
||||
/* If this is an UPSERT, then all cursors have already been opened by
|
||||
** the outer INSERT and the data cursor should be pointing at the row
|
||||
** that is to be updated. So bypass the code that searches for the
|
||||
** row(s) to be updated.
|
||||
*/
|
||||
pWInfo = 0;
|
||||
eOnePass = ONEPASS_SINGLE;
|
||||
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
|
||||
}else{
|
||||
/* Begin the database scan.
|
||||
**
|
||||
** Do not consider a single-pass strategy for a multi-row update if
|
||||
** there are any triggers or foreign keys to process, or rows may
|
||||
** be deleted as a result of REPLACE conflict handling. Any of these
|
||||
** things might disturb a cursor being used to scan through the table
|
||||
** or index, causing a single-pass approach to malfunction. */
|
||||
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* A one-pass strategy that might update more than one row may not
|
||||
** be used if any column of the index used for the scan is being
|
||||
** updated. Otherwise, if there is an index on "b", statements like
|
||||
** the following could create an infinite loop:
|
||||
**
|
||||
** UPDATE t1 SET b=b+1 WHERE b>?
|
||||
**
|
||||
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
||||
** strategy that uses an index for which one or more columns are being
|
||||
** updated. */
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
if( eOnePass!=ONEPASS_SINGLE ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
int iCur = aiCurOnePass[1];
|
||||
if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
||||
eOnePass = ONEPASS_OFF;
|
||||
}
|
||||
assert( iCur!=iDataCur || !HasRowid(pTab) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( HasRowid(pTab) ){
|
||||
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
||||
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
||||
@@ -437,7 +486,7 @@ void sqlite3Update(
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
|
||||
}
|
||||
if( eOnePass ){
|
||||
sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
nKey = nPk;
|
||||
regKey = iPk;
|
||||
}else{
|
||||
@@ -447,59 +496,58 @@ void sqlite3Update(
|
||||
}
|
||||
}
|
||||
|
||||
if( eOnePass!=ONEPASS_MULTI ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
if( !isView ){
|
||||
int addrOnce = 0;
|
||||
|
||||
/* Open every index that needs updating. */
|
||||
if( pUpsert==0 ){
|
||||
if( eOnePass!=ONEPASS_MULTI ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
int addrOnce = 0;
|
||||
|
||||
/* Open every index that needs updating. */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
||||
}
|
||||
|
||||
if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
||||
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur,
|
||||
aToOpen, 0, 0);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
||||
}
|
||||
|
||||
if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
||||
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
|
||||
0, 0);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
||||
assert( pPk );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
labelContinue = labelBreak;
|
||||
}else{
|
||||
if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
||||
assert( pPk );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
if( eOnePass!=ONEPASS_SINGLE ){
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||
VdbeCoverageIf(v, pPk==0);
|
||||
VdbeCoverageIf(v, pPk!=0);
|
||||
}else if( pPk ){
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
|
||||
regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||
VdbeCoverageIf(v, pPk==0);
|
||||
VdbeCoverageIf(v, pPk!=0);
|
||||
}else if( pPk ){
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
|
||||
regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* If the record number will change, set register regNewRowid to
|
||||
** contain the new value. If the record number is not being modified,
|
||||
/* If the rowid value will change, set register regNewRowid to
|
||||
** contain the new value. If the rowid is not being modified,
|
||||
** then regNewRowid is the same register as regOldRowid, which is
|
||||
** already populated. */
|
||||
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
|
||||
@@ -562,7 +610,7 @@ void sqlite3Update(
|
||||
*/
|
||||
testcase( i==31 );
|
||||
testcase( i==32 );
|
||||
sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
||||
}
|
||||
@@ -591,10 +639,14 @@ void sqlite3Update(
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* If it did not delete it, the row-trigger may still have modified
|
||||
/* After-BEFORE-trigger-reload-loop:
|
||||
** If it did not delete it, the BEFORE trigger may still have modified
|
||||
** some of the columns of the row being updated. Load the values for
|
||||
** all columns not modified by the update statement into their
|
||||
** registers in case this has happened.
|
||||
** all columns not modified by the update statement into their registers
|
||||
** in case this has happened. Only unmodified columns are reloaded.
|
||||
** The values computed for modified columns use the values before the
|
||||
** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
|
||||
** for an example.
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]<0 && i!=pTab->iPKey ){
|
||||
@@ -610,7 +662,7 @@ void sqlite3Update(
|
||||
assert( regOldRowid>0 );
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
||||
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
|
||||
aXRef);
|
||||
aXRef, 0);
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
if( hasFK ){
|
||||
@@ -680,7 +732,7 @@ void sqlite3Update(
|
||||
|
||||
/* Increment the row counter
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
||||
}
|
||||
|
||||
@@ -707,16 +759,15 @@ void sqlite3Update(
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
** autoincrement tables.
|
||||
*/
|
||||
if( pParse->nested==0 && pParse->pTriggerTab==0 ){
|
||||
if( pParse->nested==0 && pParse->pTriggerTab==0 && pUpsert==0 ){
|
||||
sqlite3AutoincrementEnd(pParse);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of rows that were changed. If this routine is
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
** Return the number of rows that were changed, if we are tracking
|
||||
** that information.
|
||||
*/
|
||||
if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
|
||||
@@ -728,6 +779,10 @@ update_cleanup:
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprListDelete(db, pChanges);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
||||
sqlite3ExprListDelete(db, pOrderBy);
|
||||
sqlite3ExprDelete(db, pLimit);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
/* Make sure "isView" and other macros defined above are undefined. Otherwise
|
||||
@@ -784,10 +839,10 @@ static void updateVirtualTable(
|
||||
int regRowid; /* Register for ephem table rowid */
|
||||
int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
|
||||
int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
|
||||
int bOnePass; /* True to use onepass strategy */
|
||||
int eOnePass; /* True to use onepass strategy */
|
||||
int addr; /* Address of OP_OpenEphemeral */
|
||||
|
||||
/* Allocate nArg registers to martial the arguments to VUpdate. Then
|
||||
/* Allocate nArg registers in which to gather the arguments for VUpdate. Then
|
||||
** create and open the ephemeral table in which the records created from
|
||||
** these arguments will be temporarily stored. */
|
||||
assert( v );
|
||||
@@ -803,40 +858,58 @@ static void updateVirtualTable(
|
||||
if( pWInfo==0 ) return;
|
||||
|
||||
/* Populate the argument registers. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||
if( pRowid ){
|
||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]>=0 ){
|
||||
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
||||
sqlite3VdbeChangeP5(v, 1); /* Enable sqlite3_vtab_nochange() */
|
||||
}
|
||||
}
|
||||
|
||||
bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||
|
||||
if( bOnePass ){
|
||||
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
||||
** above. Also, if this is a top-level parse (not a trigger), clear the
|
||||
** multi-write flag so that the VM does not open a statement journal */
|
||||
sqlite3VdbeChangeToNoop(v, addr);
|
||||
if( sqlite3IsToplevel(pParse) ){
|
||||
pParse->isMultiWrite = 0;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||
if( pRowid ){
|
||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||
}
|
||||
}else{
|
||||
Index *pPk; /* PRIMARY KEY index */
|
||||
i16 iPk; /* PRIMARY KEY column */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
assert( pPk->nKeyCol==1 );
|
||||
iPk = pPk->aiColumn[0];
|
||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
||||
}
|
||||
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||
|
||||
/* There is no ONEPASS_MULTI on virtual tables */
|
||||
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
||||
|
||||
if( eOnePass ){
|
||||
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
||||
** above. */
|
||||
sqlite3VdbeChangeToNoop(v, addr);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
}else{
|
||||
/* Create a record from the argument register contents and insert it into
|
||||
** the ephemeral table. */
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Signal an assert() within OP_MakeRecord that it is allowed to
|
||||
** accept no-change records with serial_type 10 */
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
|
||||
}
|
||||
|
||||
|
||||
if( bOnePass==0 ){
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
/* End the virtual table scan */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
@@ -856,7 +929,7 @@ static void updateVirtualTable(
|
||||
|
||||
/* End of the ephemeral table scan. Or, if using the onepass strategy,
|
||||
** jump to here if the scan visited zero rows. */
|
||||
if( bOnePass==0 ){
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
||||
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
** 2018-04-12
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code to implement various aspects of UPSERT
|
||||
** processing and handling of the Upsert object.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
/*
|
||||
** Free a list of Upsert objects
|
||||
*/
|
||||
void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
|
||||
if( p ){
|
||||
sqlite3ExprListDelete(db, p->pUpsertTarget);
|
||||
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
|
||||
sqlite3ExprListDelete(db, p->pUpsertSet);
|
||||
sqlite3ExprDelete(db, p->pUpsertWhere);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Duplicate an Upsert object.
|
||||
*/
|
||||
Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
|
||||
if( p==0 ) return 0;
|
||||
return sqlite3UpsertNew(db,
|
||||
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
|
||||
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
|
||||
sqlite3ExprListDup(db, p->pUpsertSet, 0),
|
||||
sqlite3ExprDup(db, p->pUpsertWhere, 0)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new Upsert object.
|
||||
*/
|
||||
Upsert *sqlite3UpsertNew(
|
||||
sqlite3 *db, /* Determines which memory allocator to use */
|
||||
ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
|
||||
Expr *pTargetWhere, /* Optional WHERE clause on the target */
|
||||
ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
|
||||
Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */
|
||||
){
|
||||
Upsert *pNew;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
|
||||
if( pNew==0 ){
|
||||
sqlite3ExprListDelete(db, pTarget);
|
||||
sqlite3ExprDelete(db, pTargetWhere);
|
||||
sqlite3ExprListDelete(db, pSet);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
return 0;
|
||||
}else{
|
||||
pNew->pUpsertTarget = pTarget;
|
||||
pNew->pUpsertTargetWhere = pTargetWhere;
|
||||
pNew->pUpsertSet = pSet;
|
||||
pNew->pUpsertWhere = pWhere;
|
||||
pNew->pUpsertIdx = 0;
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyze the ON CONFLICT clause described by pUpsert. Resolve all
|
||||
** symbols in the conflict-target.
|
||||
**
|
||||
** Return SQLITE_OK if everything works, or an error code is something
|
||||
** is wrong.
|
||||
*/
|
||||
int sqlite3UpsertAnalyzeTarget(
|
||||
Parse *pParse, /* The parsing context */
|
||||
SrcList *pTabList, /* Table into which we are inserting */
|
||||
Upsert *pUpsert /* The ON CONFLICT clauses */
|
||||
){
|
||||
Table *pTab; /* That table into which we are inserting */
|
||||
int rc; /* Result code */
|
||||
int iCursor; /* Cursor used by pTab */
|
||||
Index *pIdx; /* One of the indexes of pTab */
|
||||
ExprList *pTarget; /* The conflict-target clause */
|
||||
Expr *pTerm; /* One term of the conflict-target clause */
|
||||
NameContext sNC; /* Context for resolving symbolic names */
|
||||
Expr sCol[2]; /* Index column converted into an Expr */
|
||||
|
||||
assert( pTabList->nSrc==1 );
|
||||
assert( pTabList->a[0].pTab!=0 );
|
||||
assert( pUpsert!=0 );
|
||||
assert( pUpsert->pUpsertTarget!=0 );
|
||||
|
||||
/* Resolve all symbolic names in the conflict-target clause, which
|
||||
** includes both the list of columns and the optional partial-index
|
||||
** WHERE clause.
|
||||
*/
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Check to see if the conflict target matches the rowid. */
|
||||
pTab = pTabList->a[0].pTab;
|
||||
pTarget = pUpsert->pUpsertTarget;
|
||||
iCursor = pTabList->a[0].iCursor;
|
||||
if( HasRowid(pTab)
|
||||
&& pTarget->nExpr==1
|
||||
&& (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
|
||||
&& pTerm->iColumn==XN_ROWID
|
||||
){
|
||||
/* The conflict-target is the rowid of the primary table */
|
||||
assert( pUpsert->pUpsertIdx==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Initialize sCol[0..1] to be an expression parse tree for a
|
||||
** single column of an index. The sCol[0] node will be the TK_COLLATE
|
||||
** operator and sCol[1] will be the TK_COLUMN operator. Code below
|
||||
** will populate the specific collation and column number values
|
||||
** prior to comparing against the conflict-target expression.
|
||||
*/
|
||||
memset(sCol, 0, sizeof(sCol));
|
||||
sCol[0].op = TK_COLLATE;
|
||||
sCol[0].pLeft = &sCol[1];
|
||||
sCol[1].op = TK_COLUMN;
|
||||
sCol[1].iTable = pTabList->a[0].iCursor;
|
||||
|
||||
/* Check for matches against other indexes */
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int ii, jj, nn;
|
||||
if( !IsUniqueIndex(pIdx) ) continue;
|
||||
if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
if( pUpsert->pUpsertTargetWhere==0 ) continue;
|
||||
if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
|
||||
pIdx->pPartIdxWhere, iCursor)!=0 ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
nn = pIdx->nKeyCol;
|
||||
for(ii=0; ii<nn; ii++){
|
||||
Expr *pExpr;
|
||||
sCol[0].u.zToken = (char*)pIdx->azColl[ii];
|
||||
if( pIdx->aiColumn[ii]==XN_EXPR ){
|
||||
assert( pIdx->aColExpr!=0 );
|
||||
assert( pIdx->aColExpr->nExpr>ii );
|
||||
pExpr = pIdx->aColExpr->a[ii].pExpr;
|
||||
if( pExpr->op!=TK_COLLATE ){
|
||||
sCol[0].pLeft = pExpr;
|
||||
pExpr = &sCol[0];
|
||||
}
|
||||
}else{
|
||||
sCol[0].pLeft = &sCol[1];
|
||||
sCol[1].iColumn = pIdx->aiColumn[ii];
|
||||
pExpr = &sCol[0];
|
||||
}
|
||||
for(jj=0; jj<nn; jj++){
|
||||
if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
|
||||
break; /* Column ii of the index matches column jj of target */
|
||||
}
|
||||
}
|
||||
if( jj>=nn ){
|
||||
/* The target contains no match for column jj of the index */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( ii<nn ){
|
||||
/* Column ii of the index did not match any term of the conflict target.
|
||||
** Continue the search with the next index. */
|
||||
continue;
|
||||
}
|
||||
pUpsert->pUpsertIdx = pIdx;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
|
||||
"PRIMARY KEY or UNIQUE constraint");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate bytecode that does an UPDATE as part of an upsert.
|
||||
**
|
||||
** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
|
||||
** In this case parameter iCur is a cursor open on the table b-tree that
|
||||
** currently points to the conflicting table row. Otherwise, if pIdx
|
||||
** is not NULL, then pIdx is the constraint that failed and iCur is a
|
||||
** cursor points to the conflicting row.
|
||||
*/
|
||||
void sqlite3UpsertDoUpdate(
|
||||
Parse *pParse, /* The parsing and code-generating context */
|
||||
Upsert *pUpsert, /* The ON CONFLICT clause for the upsert */
|
||||
Table *pTab, /* The table being updated */
|
||||
Index *pIdx, /* The UNIQUE constraint that failed */
|
||||
int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3 *db = pParse->db;
|
||||
SrcList *pSrc; /* FROM clause for the UPDATE */
|
||||
int iDataCur;
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pUpsert!=0 );
|
||||
VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
|
||||
iDataCur = pUpsert->iDataCur;
|
||||
if( pIdx && iCur!=iDataCur ){
|
||||
if( HasRowid(pTab) ){
|
||||
int regRowid = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
int nPk = pPk->nKeyCol;
|
||||
int iPk = pParse->nMem+1;
|
||||
int i;
|
||||
pParse->nMem += nPk;
|
||||
for(i=0; i<nPk; i++){
|
||||
int k;
|
||||
assert( pPk->aiColumn[i]>=0 );
|
||||
k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
|
||||
VdbeComment((v, "%s.%s", pIdx->zName,
|
||||
pTab->aCol[pPk->aiColumn[i]].zName));
|
||||
}
|
||||
sqlite3VdbeVerifyAbortable(v, OE_Abort);
|
||||
i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
|
||||
"corrupt database", P4_STATIC);
|
||||
sqlite3VdbeJumpHere(v, i);
|
||||
}
|
||||
}
|
||||
/* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
|
||||
** we have to make a copy before passing it down into sqlite3Update() */
|
||||
pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
|
||||
sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
|
||||
pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
|
||||
pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
|
||||
pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */
|
||||
VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_UPSERT */
|
||||
+101
-44
@@ -320,6 +320,45 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
||||
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute 10 to the E-th power. Examples: E==1 results in 10.
|
||||
** E==2 results in 100. E==50 results in 1.0e50.
|
||||
**
|
||||
** This routine only works for values of E between 1 and 341.
|
||||
*/
|
||||
static LONGDOUBLE_TYPE sqlite3Pow10(int E){
|
||||
#if defined(_MSC_VER)
|
||||
static const LONGDOUBLE_TYPE x[] = {
|
||||
1.0e+001,
|
||||
1.0e+002,
|
||||
1.0e+004,
|
||||
1.0e+008,
|
||||
1.0e+016,
|
||||
1.0e+032,
|
||||
1.0e+064,
|
||||
1.0e+128,
|
||||
1.0e+256
|
||||
};
|
||||
LONGDOUBLE_TYPE r = 1.0;
|
||||
int i;
|
||||
assert( E>=0 && E<=307 );
|
||||
for(i=0; E!=0; i++, E >>=1){
|
||||
if( E & 1 ) r *= x[i];
|
||||
}
|
||||
return r;
|
||||
#else
|
||||
LONGDOUBLE_TYPE x = 10.0;
|
||||
LONGDOUBLE_TYPE r = 1.0;
|
||||
while(1){
|
||||
if( E & 1 ) r *= x;
|
||||
E >>= 1;
|
||||
if( E==0 ) break;
|
||||
x *= x;
|
||||
}
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** The string z[] is an text representation of a real number.
|
||||
** Convert this string to a double and write it into *pResult.
|
||||
@@ -387,12 +426,12 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
/* copy max significant digits to significand */
|
||||
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z+=incr, nDigits++;
|
||||
z+=incr; nDigits++;
|
||||
}
|
||||
|
||||
/* skip non-significant significand digits
|
||||
** (increase exponent by d to shift decimal left) */
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; nDigits++; d++; }
|
||||
if( z>=zEnd ) goto do_atof_calc;
|
||||
|
||||
/* if decimal point is present */
|
||||
@@ -405,7 +444,7 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
s = s*10 + (*z - '0');
|
||||
d--;
|
||||
}
|
||||
z+=incr, nDigits++;
|
||||
z+=incr; nDigits++;
|
||||
}
|
||||
}
|
||||
if( z>=zEnd ) goto do_atof_calc;
|
||||
@@ -475,11 +514,10 @@ do_atof_calc:
|
||||
if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
result = (double)s;
|
||||
}else{
|
||||
LONGDOUBLE_TYPE scale = 1.0;
|
||||
/* attempt to handle extremely small/large numbers better */
|
||||
if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
result /= 1.0e+308;
|
||||
@@ -491,14 +529,15 @@ do_atof_calc:
|
||||
if( esign<0 ){
|
||||
result = 0.0*s;
|
||||
}else{
|
||||
#ifdef INFINITY
|
||||
result = INFINITY*s;
|
||||
#else
|
||||
result = 1e308*1e308*s; /* Infinity */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* 1.0e+22 is the largest power of 10 than can be
|
||||
** represented exactly. */
|
||||
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
||||
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
}else{
|
||||
@@ -553,16 +592,12 @@ static int compare2pow63(const char *zNum, int incr){
|
||||
** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
|
||||
** routine does *not* accept hexadecimal notation.
|
||||
**
|
||||
** If the zNum value is representable as a 64-bit twos-complement
|
||||
** integer, then write that value into *pNum and return 0.
|
||||
** Returns:
|
||||
**
|
||||
** If zNum is exactly 9223372036854775808, return 2. This special
|
||||
** case is broken out because while 9223372036854775808 cannot be a
|
||||
** signed 64-bit integer, its negative -9223372036854775808 can be.
|
||||
**
|
||||
** If zNum is too big for a 64-bit integer and is not
|
||||
** 9223372036854775808 or if zNum contains any non-numeric text,
|
||||
** then return 1.
|
||||
** 0 Successful transformation. Fits in a 64-bit signed integer.
|
||||
** 1 Excess non-space text after the integer value
|
||||
** 2 Integer too large for a 64-bit signed integer or is malformed
|
||||
** 3 Special case of 9223372036854775808
|
||||
**
|
||||
** length is the number of bytes in the string (bytes, not characters).
|
||||
** The string is not necessarily zero-terminated. The encoding is
|
||||
@@ -575,6 +610,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
int i;
|
||||
int c = 0;
|
||||
int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
|
||||
int rc; /* Baseline return code */
|
||||
const char *zStart;
|
||||
const char *zEnd = zNum + length;
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
@@ -602,43 +638,57 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
|
||||
u = u*10 + c - '0';
|
||||
}
|
||||
testcase( i==18*incr );
|
||||
testcase( i==19*incr );
|
||||
testcase( i==20*incr );
|
||||
if( u>LARGEST_INT64 ){
|
||||
/* This test and assignment is needed only to suppress UB warnings
|
||||
** from clang and -fsanitize=undefined. This test and assignment make
|
||||
** the code a little larger and slower, and no harm comes from omitting
|
||||
** them, but we must appaise the undefined-behavior pharisees. */
|
||||
*pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
||||
}else if( neg ){
|
||||
*pNum = -(i64)u;
|
||||
}else{
|
||||
*pNum = (i64)u;
|
||||
}
|
||||
testcase( i==18 );
|
||||
testcase( i==19 );
|
||||
testcase( i==20 );
|
||||
if( &zNum[i]<zEnd /* Extra bytes at the end */
|
||||
|| (i==0 && zStart==zNum) /* No digits */
|
||||
|| i>19*incr /* Too many digits */
|
||||
rc = 0;
|
||||
if( (i==0 && zStart==zNum) /* No digits */
|
||||
|| nonNum /* UTF16 with high-order bytes non-zero */
|
||||
){
|
||||
/* zNum is empty or contains non-numeric text or is longer
|
||||
** than 19 digits (thus guaranteeing that it is too large) */
|
||||
return 1;
|
||||
}else if( i<19*incr ){
|
||||
rc = 1;
|
||||
}else if( &zNum[i]<zEnd ){ /* Extra bytes at the end */
|
||||
int jj = i;
|
||||
do{
|
||||
if( !sqlite3Isspace(zNum[jj]) ){
|
||||
rc = 1; /* Extra non-space text after the integer */
|
||||
break;
|
||||
}
|
||||
jj += incr;
|
||||
}while( &zNum[jj]<zEnd );
|
||||
}
|
||||
if( i<19*incr ){
|
||||
/* Less than 19 digits, so we know that it fits in 64 bits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
return rc;
|
||||
}else{
|
||||
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
||||
c = compare2pow63(zNum, incr);
|
||||
c = i>19*incr ? 1 : compare2pow63(zNum, incr);
|
||||
if( c<0 ){
|
||||
/* zNum is less than 9223372036854775808 so it fits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
}else if( c>0 ){
|
||||
/* zNum is greater than 9223372036854775808 so it overflows */
|
||||
return 1;
|
||||
return rc;
|
||||
}else{
|
||||
/* zNum is exactly 9223372036854775808. Fits if negative. The
|
||||
** special case 2 overflow if positive */
|
||||
assert( u-1==LARGEST_INT64 );
|
||||
return neg ? 0 : 2;
|
||||
*pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
||||
if( c>0 ){
|
||||
/* zNum is greater than 9223372036854775808 so it overflows */
|
||||
return 2;
|
||||
}else{
|
||||
/* zNum is exactly 9223372036854775808. Fits if negative. The
|
||||
** special case 2 overflow if positive */
|
||||
assert( u-1==LARGEST_INT64 );
|
||||
return neg ? rc : 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,8 +701,9 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
** Returns:
|
||||
**
|
||||
** 0 Successful transformation. Fits in a 64-bit signed integer.
|
||||
** 1 Integer too large for a 64-bit signed integer or is malformed
|
||||
** 2 Special case of 9223372036854775808
|
||||
** 1 Excess text after the integer value
|
||||
** 2 Integer too large for a 64-bit signed integer or is malformed
|
||||
** 3 Special case of 9223372036854775808
|
||||
*/
|
||||
int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
|
||||
#ifndef SQLITE_OMIT_HEX_INTEGER
|
||||
@@ -666,7 +717,7 @@ int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
|
||||
u = u*16 + sqlite3HexToInt(z[k]);
|
||||
}
|
||||
memcpy(pOut, &u, 8);
|
||||
return (z[k]==0 && k-i<=16) ? 0 : 1;
|
||||
return (z[k]==0 && k-i<=16) ? 0 : 2;
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_HEX_INTEGER */
|
||||
{
|
||||
@@ -1276,7 +1327,7 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
|
||||
** overflow, leave *pA unchanged and return 1.
|
||||
*/
|
||||
int sqlite3AddInt64(i64 *pA, i64 iB){
|
||||
#if GCC_VERSION>=5004000
|
||||
#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
||||
return __builtin_add_overflow(*pA, iB, pA);
|
||||
#else
|
||||
i64 iA = *pA;
|
||||
@@ -1296,7 +1347,7 @@ int sqlite3AddInt64(i64 *pA, i64 iB){
|
||||
#endif
|
||||
}
|
||||
int sqlite3SubInt64(i64 *pA, i64 iB){
|
||||
#if GCC_VERSION>=5004000
|
||||
#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
||||
return __builtin_sub_overflow(*pA, iB, pA);
|
||||
#else
|
||||
testcase( iB==SMALLEST_INT64+1 );
|
||||
@@ -1311,7 +1362,7 @@ int sqlite3SubInt64(i64 *pA, i64 iB){
|
||||
#endif
|
||||
}
|
||||
int sqlite3MulInt64(i64 *pA, i64 iB){
|
||||
#if GCC_VERSION>=5004000
|
||||
#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
||||
return __builtin_mul_overflow(*pA, iB, pA);
|
||||
#else
|
||||
i64 iA = *pA;
|
||||
@@ -1413,8 +1464,14 @@ LogEst sqlite3LogEst(u64 x){
|
||||
if( x<2 ) return 0;
|
||||
while( x<8 ){ y -= 10; x <<= 1; }
|
||||
}else{
|
||||
#if GCC_VERSION>=5004000
|
||||
int i = 60 - __builtin_clzll(x);
|
||||
y += i*10;
|
||||
x >>= i;
|
||||
#else
|
||||
while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/
|
||||
while( x>15 ){ y += 10; x >>= 1; }
|
||||
#endif
|
||||
}
|
||||
return a[x&7] + y - 10;
|
||||
}
|
||||
|
||||
+18
-9
@@ -39,8 +39,14 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
||||
const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
|
||||
assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
|
||||
if( zSubSql ){
|
||||
assert( zSubSql[0]!='S' );
|
||||
/* The secondary SQL must be one of CREATE TABLE, CREATE INDEX,
|
||||
** or INSERT. Historically there have been attacks that first
|
||||
** corrupt the sqlite_master.sql field with other kinds of statements
|
||||
** then run VACUUM to get those statements to execute at inappropriate
|
||||
** times. */
|
||||
if( zSubSql
|
||||
&& (strncmp(zSubSql,"CRE",3)==0 || strncmp(zSubSql,"INS",3)==0)
|
||||
){
|
||||
rc = execSql(db, pzErrMsg, zSubSql);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
@@ -130,7 +136,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
Btree *pMain; /* The database being vacuumed */
|
||||
Btree *pTemp; /* The temporary database we vacuum into */
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
u16 saved_mDbFlags; /* Saved value of db->mDbFlags */
|
||||
u32 saved_flags; /* Saved value of db->flags */
|
||||
int saved_nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
||||
u8 saved_mTrace; /* Saved trace settings */
|
||||
@@ -153,11 +160,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
** restored before returning. Then set the writable-schema flag, and
|
||||
** disable CHECK and foreign key constraints. */
|
||||
saved_flags = db->flags;
|
||||
saved_mDbFlags = db->mDbFlags;
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_mTrace = db->mTrace;
|
||||
db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
|
||||
| SQLITE_PreferBuiltin | SQLITE_Vacuum);
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
|
||||
db->mTrace = 0;
|
||||
|
||||
@@ -216,7 +224,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
*/
|
||||
rc = execSql(db, pzErrMsg, "BEGIN");
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = sqlite3BtreeBeginTrans(pMain, 2);
|
||||
rc = sqlite3BtreeBeginTrans(pMain, 2, 0);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
/* Do not attempt to change the page size for a WAL database */
|
||||
@@ -251,7 +259,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = execSqlF(db, pzErrMsg,
|
||||
"SELECT sql FROM \"%w\".sqlite_master"
|
||||
" WHERE type='index' AND length(sql)>10",
|
||||
" WHERE type='index'",
|
||||
zDbMain
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
@@ -268,8 +276,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
"WHERE type='table'AND coalesce(rootpage,1)>0",
|
||||
zDbMain
|
||||
);
|
||||
assert( (db->flags & SQLITE_Vacuum)!=0 );
|
||||
db->flags &= ~SQLITE_Vacuum;
|
||||
assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 );
|
||||
db->mDbFlags &= ~DBFLAG_Vacuum;
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
/* Copy the triggers, views, and virtual tables from the main database
|
||||
@@ -337,6 +345,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
end_of_vacuum:
|
||||
/* Restore the original value of db->flags */
|
||||
db->init.iDb = 0;
|
||||
db->mDbFlags = saved_mDbFlags;
|
||||
db->flags = saved_flags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
|
||||
+675
-328
File diff suppressed because it is too large
Load Diff
+57
-4
@@ -73,7 +73,8 @@ struct VdbeOp {
|
||||
u64 cycles; /* Total time spent executing this instruction */
|
||||
#endif
|
||||
#ifdef SQLITE_VDBE_COVERAGE
|
||||
int iSrcLine; /* Source-code line that generated this opcode */
|
||||
u32 iSrcLine; /* Source-code line that generated this opcode
|
||||
** with flags in the upper 8 bits */
|
||||
#endif
|
||||
};
|
||||
typedef struct VdbeOp VdbeOp;
|
||||
@@ -127,6 +128,7 @@ typedef struct VdbeOpList VdbeOpList;
|
||||
#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */
|
||||
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
|
||||
#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */
|
||||
#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */
|
||||
|
||||
/* Error message codes for OP_Halt */
|
||||
#define P5_ConstraintNotNull 1
|
||||
@@ -196,7 +198,24 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int);
|
||||
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
|
||||
# define sqlite3VdbeVerifyNoResultRow(A)
|
||||
#endif
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
||||
#if defined(SQLITE_DEBUG)
|
||||
void sqlite3VdbeVerifyAbortable(Vdbe *p, int);
|
||||
#else
|
||||
# define sqlite3VdbeVerifyAbortable(A,B)
|
||||
#endif
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
void sqlite3VdbeExplain(Parse*,u8,const char*,...);
|
||||
void sqlite3VdbeExplainPop(Parse*);
|
||||
int sqlite3VdbeExplainParent(Parse*);
|
||||
# define ExplainQueryPlan(P) sqlite3VdbeExplain P
|
||||
# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
|
||||
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
|
||||
#else
|
||||
# define ExplainQueryPlan(P)
|
||||
# define ExplainQueryPlanPop(P)
|
||||
# define ExplainQueryPlanParent(P) 0
|
||||
#endif
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
@@ -240,6 +259,7 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
|
||||
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
|
||||
#endif
|
||||
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
|
||||
int sqlite3BlobCompare(const Mem*, const Mem*);
|
||||
|
||||
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
|
||||
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
||||
@@ -295,23 +315,52 @@ int sqlite3NotPureFunc(sqlite3_context*);
|
||||
**
|
||||
** VdbeCoverageNeverTaken(v) // Previous branch is never taken
|
||||
**
|
||||
** VdbeCoverageNeverNull(v) // Previous three-way branch is only
|
||||
** // taken on the first two ways. The
|
||||
** // NULL option is not possible
|
||||
**
|
||||
** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested
|
||||
** // in distingishing equal and not-equal.
|
||||
**
|
||||
** Every VDBE branch operation must be tagged with one of the macros above.
|
||||
** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and
|
||||
** -DSQLITE_DEBUG then an ALWAYS() will fail in the vdbeTakeBranch()
|
||||
** routine in vdbe.c, alerting the developer to the missed tag.
|
||||
**
|
||||
** During testing, the test application will invoke
|
||||
** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback
|
||||
** routine that is invoked as each bytecode branch is taken. The callback
|
||||
** contains the sqlite3.c source line number ov the VdbeCoverage macro and
|
||||
** flags to indicate whether or not the branch was taken. The test application
|
||||
** is responsible for keeping track of this and reporting byte-code branches
|
||||
** that are never taken.
|
||||
**
|
||||
** See the VdbeBranchTaken() macro and vdbeTakeBranch() function in the
|
||||
** vdbe.c source file for additional information.
|
||||
*/
|
||||
#ifdef SQLITE_VDBE_COVERAGE
|
||||
void sqlite3VdbeSetLineNumber(Vdbe*,int);
|
||||
# define VdbeCoverage(v) sqlite3VdbeSetLineNumber(v,__LINE__)
|
||||
# define VdbeCoverageIf(v,x) if(x)sqlite3VdbeSetLineNumber(v,__LINE__)
|
||||
# define VdbeCoverageAlwaysTaken(v) sqlite3VdbeSetLineNumber(v,2);
|
||||
# define VdbeCoverageNeverTaken(v) sqlite3VdbeSetLineNumber(v,1);
|
||||
# define VdbeCoverageAlwaysTaken(v) \
|
||||
sqlite3VdbeSetLineNumber(v,__LINE__|0x5000000);
|
||||
# define VdbeCoverageNeverTaken(v) \
|
||||
sqlite3VdbeSetLineNumber(v,__LINE__|0x6000000);
|
||||
# define VdbeCoverageNeverNull(v) \
|
||||
sqlite3VdbeSetLineNumber(v,__LINE__|0x4000000);
|
||||
# define VdbeCoverageNeverNullIf(v,x) \
|
||||
if(x)sqlite3VdbeSetLineNumber(v,__LINE__|0x4000000);
|
||||
# define VdbeCoverageEqNe(v) \
|
||||
sqlite3VdbeSetLineNumber(v,__LINE__|0x8000000);
|
||||
# define VDBE_OFFSET_LINENO(x) (__LINE__+x)
|
||||
#else
|
||||
# define VdbeCoverage(v)
|
||||
# define VdbeCoverageIf(v,x)
|
||||
# define VdbeCoverageAlwaysTaken(v)
|
||||
# define VdbeCoverageNeverTaken(v)
|
||||
# define VdbeCoverageNeverNull(v)
|
||||
# define VdbeCoverageNeverNullIf(v,x)
|
||||
# define VdbeCoverageEqNe(v)
|
||||
# define VDBE_OFFSET_LINENO(x) 0
|
||||
#endif
|
||||
|
||||
@@ -321,4 +370,8 @@ void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
|
||||
# define sqlite3VdbeScanStatus(a,b,c,d,e)
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
void sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_VDBE_H */
|
||||
|
||||
+45
-20
@@ -85,6 +85,7 @@ struct VdbeCursor {
|
||||
Bool isEphemeral:1; /* True for an ephemeral table */
|
||||
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
|
||||
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
|
||||
Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */
|
||||
Btree *pBtx; /* Separate file holding temporary table */
|
||||
i64 seqCount; /* Sequence counter */
|
||||
int *aAltMap; /* Mapping from table to index column numbers */
|
||||
@@ -96,18 +97,18 @@ struct VdbeCursor {
|
||||
u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
|
||||
int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0
|
||||
** if there have been no prior seeks on the cursor. */
|
||||
/* NB: seekResult does not distinguish between "no seeks have ever occurred
|
||||
** on this cursor" and "the most recent seek was an exact match". */
|
||||
/* seekResult does not distinguish between "no seeks have ever occurred
|
||||
** on this cursor" and "the most recent seek was an exact match".
|
||||
** For CURTYPE_PSEUDO, seekResult is the register holding the record */
|
||||
|
||||
/* When a new VdbeCursor is allocated, only the fields above are zeroed.
|
||||
** The fields that follow are uninitialized, and must be individually
|
||||
** initialized prior to first use. */
|
||||
VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
|
||||
union {
|
||||
BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */
|
||||
sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
|
||||
int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */
|
||||
VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
|
||||
BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */
|
||||
sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
|
||||
VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
|
||||
} uc;
|
||||
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
|
||||
u32 iHdrOffset; /* Offset to next unparsed byte of the header */
|
||||
@@ -168,6 +169,9 @@ struct VdbeFrame {
|
||||
void *token; /* Copy of SubProgram.token */
|
||||
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
|
||||
AuxData *pAuxData; /* Linked list of auxdata allocations */
|
||||
#if SQLITE_DEBUG
|
||||
u32 iFrameMagic; /* magic number for sanity checking */
|
||||
#endif
|
||||
int nCursor; /* Number of entries in apCsr */
|
||||
int pc; /* Program Counter in parent (calling) frame */
|
||||
int nOp; /* Size of aOp array */
|
||||
@@ -178,6 +182,13 @@ struct VdbeFrame {
|
||||
int nDbChange; /* Value of db->nChange */
|
||||
};
|
||||
|
||||
/* Magic number for sanity checking on VdbeFrame objects */
|
||||
#define SQLITE_FRAME_MAGIC 0x879fb71e
|
||||
|
||||
/*
|
||||
** Return a pointer to the array of registers allocated for use
|
||||
** by a VdbeFrame.
|
||||
*/
|
||||
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
|
||||
|
||||
/*
|
||||
@@ -192,8 +203,6 @@ struct sqlite3_value {
|
||||
int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
|
||||
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
|
||||
FuncDef *pDef; /* Used only when flags==MEM_Agg */
|
||||
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
|
||||
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
|
||||
} u;
|
||||
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
||||
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
|
||||
@@ -208,7 +217,7 @@ struct sqlite3_value {
|
||||
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
|
||||
#ifdef SQLITE_DEBUG
|
||||
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
|
||||
void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
|
||||
u16 mScopyFlags; /* flags value immediately after the shallow copy */
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -237,8 +246,8 @@ struct sqlite3_value {
|
||||
#define MEM_Real 0x0008 /* Value is a real number */
|
||||
#define MEM_Blob 0x0010 /* Value is a BLOB */
|
||||
#define MEM_AffMask 0x001f /* Mask of affinity bits */
|
||||
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
|
||||
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
|
||||
/* Available 0x0020 */
|
||||
/* Available 0x0040 */
|
||||
#define MEM_Undefined 0x0080 /* Value is undefined */
|
||||
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
|
||||
#define MEM_TypeMask 0xc1ff /* Mask of type bits */
|
||||
@@ -265,7 +274,7 @@ struct sqlite3_value {
|
||||
** that needs to be deallocated to avoid a leak.
|
||||
*/
|
||||
#define VdbeMemDynamic(X) \
|
||||
(((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
|
||||
(((X)->flags&(MEM_Agg|MEM_Dyn))!=0)
|
||||
|
||||
/*
|
||||
** Clear any existing type flags from a Mem and replace them with f
|
||||
@@ -317,7 +326,6 @@ struct sqlite3_context {
|
||||
int iOp; /* Instruction number of OP_Function */
|
||||
int isError; /* Error code returned by the function. */
|
||||
u8 skipFlag; /* Skip accumulator loading if true */
|
||||
u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
|
||||
u8 argc; /* Number of arguments */
|
||||
sqlite3_value *argv[1]; /* Argument set */
|
||||
};
|
||||
@@ -380,14 +388,15 @@ struct Vdbe {
|
||||
int nOp; /* Number of instructions in the program */
|
||||
#ifdef SQLITE_DEBUG
|
||||
int rcApp; /* errcode set by sqlite3_result_error_code() */
|
||||
u32 nWrite; /* Number of write operations that have occurred */
|
||||
#endif
|
||||
u16 nResColumn; /* Number of columns in one row of the result set */
|
||||
u8 errorAction; /* Recovery action to do in case of an error */
|
||||
u8 minWriteFileFormat; /* Minimum file format for writable database files */
|
||||
u8 prepFlags; /* SQLITE_PREPARE_* flags */
|
||||
bft expired:1; /* True if the VM needs to be recompiled */
|
||||
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
|
||||
bft expired:2; /* 1: recompile VM immediately 2: when convenient */
|
||||
bft explain:2; /* True if EXPLAIN present on SQL command */
|
||||
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
|
||||
bft changeCntOn:1; /* True to update the change-counter */
|
||||
bft runOnlyOnce:1; /* Automatically expire on reset */
|
||||
bft usesStmtJournal:1; /* True if uses a statement journal */
|
||||
@@ -448,9 +457,6 @@ void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
|
||||
void sqliteVdbePopStack(Vdbe*,int);
|
||||
int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
|
||||
int sqlite3VdbeCursorRestore(VdbeCursor*);
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
void sqlite3VdbePrintOp(FILE*, int, Op*);
|
||||
#endif
|
||||
u32 sqlite3VdbeSerialTypeLen(u32);
|
||||
u8 sqlite3VdbeOneByteSerialTypeLen(u8);
|
||||
u32 sqlite3VdbeSerialType(Mem*, int, u32*);
|
||||
@@ -481,12 +487,16 @@ void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*));
|
||||
void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
|
||||
void sqlite3VdbeMemSetNull(Mem*);
|
||||
void sqlite3VdbeMemSetZeroBlob(Mem*,int);
|
||||
void sqlite3VdbeMemSetRowSet(Mem*);
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3VdbeMemIsRowSet(const Mem*);
|
||||
#endif
|
||||
int sqlite3VdbeMemSetRowSet(Mem*);
|
||||
int sqlite3VdbeMemMakeWriteable(Mem*);
|
||||
int sqlite3VdbeMemStringify(Mem*, u8, u8);
|
||||
i64 sqlite3VdbeIntValue(Mem*);
|
||||
int sqlite3VdbeMemIntegerify(Mem*);
|
||||
double sqlite3VdbeRealValue(Mem*);
|
||||
int sqlite3VdbeBooleanValue(Mem*, int ifNull);
|
||||
void sqlite3VdbeIntegerAffinity(Mem*);
|
||||
int sqlite3VdbeMemRealify(Mem*);
|
||||
int sqlite3VdbeMemNumerify(Mem*);
|
||||
@@ -494,11 +504,18 @@ void sqlite3VdbeMemCast(Mem*,u8,u8);
|
||||
int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*);
|
||||
void sqlite3VdbeMemRelease(Mem *p);
|
||||
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
|
||||
#endif
|
||||
const char *sqlite3OpcodeName(int);
|
||||
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
||||
int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
|
||||
int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3VdbeFrameIsValid(VdbeFrame*);
|
||||
#endif
|
||||
void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */
|
||||
void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
|
||||
@@ -514,6 +531,14 @@ int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
|
||||
void sqlite3VdbeAssertAbortable(Vdbe*);
|
||||
#else
|
||||
# define sqlite3VdbeIncrWriteCounter(V,C)
|
||||
# define sqlite3VdbeAssertAbortable(V)
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
void sqlite3VdbeEnter(Vdbe*);
|
||||
#else
|
||||
|
||||
+55
-61
@@ -268,6 +268,11 @@ int sqlite3_value_type(sqlite3_value* pVal){
|
||||
return aType[pVal->flags&MEM_AffMask];
|
||||
}
|
||||
|
||||
/* Return true if a parameter to xUpdate represents an unchanged column */
|
||||
int sqlite3_value_nochange(sqlite3_value *pVal){
|
||||
return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero);
|
||||
}
|
||||
|
||||
/* Make a copy of an sqlite3_value object
|
||||
*/
|
||||
sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
|
||||
@@ -367,14 +372,12 @@ void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
|
||||
void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
|
||||
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
||||
pCtx->isError = SQLITE_ERROR;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
|
||||
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
||||
pCtx->isError = SQLITE_ERROR;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
|
||||
}
|
||||
#endif
|
||||
@@ -480,8 +483,7 @@ int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
|
||||
pCtx->isError = errCode;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
pCtx->isError = errCode ? errCode : -1;
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
|
||||
#endif
|
||||
@@ -495,7 +497,6 @@ void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
|
||||
void sqlite3_result_error_toobig(sqlite3_context *pCtx){
|
||||
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
||||
pCtx->isError = SQLITE_TOOBIG;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
}
|
||||
@@ -505,7 +506,6 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){
|
||||
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
||||
sqlite3VdbeMemSetNull(pCtx->pOut);
|
||||
pCtx->isError = SQLITE_NOMEM_BKPT;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
sqlite3OomFault(pCtx->pOut->db);
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ static int doWalCallbacks(sqlite3 *db){
|
||||
sqlite3BtreeEnter(pBt);
|
||||
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
|
||||
if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){
|
||||
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
|
||||
}
|
||||
}
|
||||
@@ -634,7 +634,7 @@ static int sqlite3Step(Vdbe *p){
|
||||
if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
|
||||
#endif
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
if( rc==SQLITE_DONE && db->autoCommit ){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
p->rc = doWalCallbacks(db);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
@@ -678,7 +678,6 @@ end_of_step:
|
||||
*/
|
||||
int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
int rc = SQLITE_OK; /* Result from sqlite3Step() */
|
||||
int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */
|
||||
Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */
|
||||
int cnt = 0; /* Counter to prevent infinite loop of reprepares */
|
||||
sqlite3 *db; /* The database connection */
|
||||
@@ -692,32 +691,31 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
|
||||
int savedPc = v->pc;
|
||||
rc2 = rc = sqlite3Reprepare(v);
|
||||
if( rc!=SQLITE_OK) break;
|
||||
rc = sqlite3Reprepare(v);
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
** from the database handle into the statement and sets the statement
|
||||
** program counter to 0 to ensure that when the statement is
|
||||
** finalized or reset the parser error message is available via
|
||||
** sqlite3_errmsg() and sqlite3_errcode().
|
||||
*/
|
||||
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
||||
sqlite3DbFree(db, v->zErrMsg);
|
||||
if( !db->mallocFailed ){
|
||||
v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
||||
v->rc = rc = sqlite3ApiExit(db, rc);
|
||||
} else {
|
||||
v->zErrMsg = 0;
|
||||
v->rc = rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
if( savedPc>=0 ) v->doingRerun = 1;
|
||||
assert( v->expired==0 );
|
||||
}
|
||||
if( rc2!=SQLITE_OK ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
** from the database handle into the statement and sets the statement
|
||||
** program counter to 0 to ensure that when the statement is
|
||||
** finalized or reset the parser error message is available via
|
||||
** sqlite3_errmsg() and sqlite3_errcode().
|
||||
*/
|
||||
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
||||
sqlite3DbFree(db, v->zErrMsg);
|
||||
if( !db->mallocFailed ){
|
||||
v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
||||
v->rc = rc2;
|
||||
} else {
|
||||
v->zErrMsg = 0;
|
||||
v->rc = rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
@@ -747,6 +745,25 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
|
||||
return p->pOut->db;
|
||||
}
|
||||
|
||||
/*
|
||||
** If this routine is invoked from within an xColumn method of a virtual
|
||||
** table, then it returns true if and only if the the call is during an
|
||||
** UPDATE operation and the value of the column will not be modified
|
||||
** by the UPDATE.
|
||||
**
|
||||
** If this routine is called from any context other than within the
|
||||
** xColumn method of a virtual table, then the return value is meaningless
|
||||
** and arbitrary.
|
||||
**
|
||||
** Virtual table implements might use this routine to optimize their
|
||||
** performance by substituting a NULL result, or some other light-weight
|
||||
** value, as a signal to the xUpdate routine that the column is unchanged.
|
||||
*/
|
||||
int sqlite3_vtab_nochange(sqlite3_context *p){
|
||||
assert( p );
|
||||
return sqlite3_value_nochange(p->pOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time for a statement. If the current time
|
||||
** is requested more than once within the same run of a single prepared
|
||||
@@ -770,28 +787,6 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
|
||||
return *piTime;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following is the implementation of an SQL function that always
|
||||
** fails with an error message stating that the function is used in the
|
||||
** wrong context. The sqlite3_overload_function() API might construct
|
||||
** SQL function that use this routine so that the functions will exist
|
||||
** for name resolution but are actually overloaded by the xFindFunction
|
||||
** method of virtual tables.
|
||||
*/
|
||||
void sqlite3InvalidFunction(
|
||||
sqlite3_context *context, /* The function calling context */
|
||||
int NotUsed, /* Number of arguments to the function */
|
||||
sqlite3_value **NotUsed2 /* Value of each argument */
|
||||
){
|
||||
const char *zName = context->pFunc->zName;
|
||||
char *zErr;
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
zErr = sqlite3_mprintf(
|
||||
"unable to use function %s in the requested context", zName);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new aggregate context for p and return a pointer to
|
||||
** its pMem->z element.
|
||||
@@ -895,10 +890,7 @@ void sqlite3_set_auxdata(
|
||||
pAuxData->iAuxArg = iArg;
|
||||
pAuxData->pNextAux = pVdbe->pAuxData;
|
||||
pVdbe->pAuxData = pAuxData;
|
||||
if( pCtx->fErrorOrAux==0 ){
|
||||
pCtx->isError = 0;
|
||||
pCtx->fErrorOrAux = 1;
|
||||
}
|
||||
if( pCtx->isError==0 ) pCtx->isError = -1;
|
||||
}else if( pAuxData->xDeleteAux ){
|
||||
pAuxData->xDeleteAux(pAuxData->pAux);
|
||||
}
|
||||
@@ -978,7 +970,7 @@ static const Mem *columnNullValue(void){
|
||||
/* .xDel = */ (void(*)(void*))0,
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* .pScopyFrom = */ (Mem*)0,
|
||||
/* .pFiller = */ (void*)0,
|
||||
/* .mScopyFlags= */ 0,
|
||||
#endif
|
||||
};
|
||||
return &nullMem;
|
||||
@@ -1654,7 +1646,9 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
|
||||
Vdbe *pVdbe = (Vdbe*)pStmt;
|
||||
u32 v;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !pStmt ){
|
||||
if( !pStmt
|
||||
|| (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter)))
|
||||
){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
@@ -1723,7 +1717,7 @@ static UnpackedRecord *vdbeUnpackRecord(
|
||||
|
||||
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
|
||||
if( pRet ){
|
||||
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
|
||||
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
|
||||
}
|
||||
return pRet;
|
||||
@@ -1796,7 +1790,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
*/
|
||||
int sqlite3_preupdate_count(sqlite3 *db){
|
||||
PreUpdate *p = db->pPreUpdate;
|
||||
return (p ? p->keyinfo.nField : 0);
|
||||
return (p ? p->keyinfo.nKeyField : 0);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
|
||||
+370
-219
File diff suppressed because it is too large
Load Diff
+24
-26
@@ -63,11 +63,12 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
|
||||
v->aMem[1].u.i = iRow;
|
||||
|
||||
/* If the statement has been run before (and is paused at the OP_ResultRow)
|
||||
** then back it up to the point where it does the OP_SeekRowid. This could
|
||||
** then back it up to the point where it does the OP_NotExists. This could
|
||||
** have been down with an extra OP_Goto, but simply setting the program
|
||||
** counter is faster. */
|
||||
if( v->pc>3 ){
|
||||
v->pc = 3;
|
||||
if( v->pc>4 ){
|
||||
v->pc = 4;
|
||||
assert( v->aOp[v->pc].opcode==OP_NotExists );
|
||||
rc = sqlite3VdbeExec(v);
|
||||
}else{
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
@@ -129,8 +130,8 @@ int sqlite3_blob_open(
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
Table *pTab;
|
||||
Parse *pParse = 0;
|
||||
Incrblob *pBlob = 0;
|
||||
Parse sParse;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppBlob==0 ){
|
||||
@@ -148,37 +149,34 @@ int sqlite3_blob_open(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
|
||||
if( !pParse ) goto blob_open_out;
|
||||
|
||||
do {
|
||||
memset(pParse, 0, sizeof(Parse));
|
||||
pParse->db = db;
|
||||
memset(&sParse, 0, sizeof(Parse));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
sParse.db = db;
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = 0;
|
||||
|
||||
sqlite3BtreeEnterAll(db);
|
||||
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
|
||||
pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
|
||||
if( pTab && IsVirtual(pTab) ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
|
||||
}
|
||||
if( pTab && !HasRowid(pTab) ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( pTab && pTab->pSelect ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
|
||||
}
|
||||
#endif
|
||||
if( !pTab ){
|
||||
if( pParse->zErrMsg ){
|
||||
if( sParse.zErrMsg ){
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = pParse->zErrMsg;
|
||||
pParse->zErrMsg = 0;
|
||||
zErr = sParse.zErrMsg;
|
||||
sParse.zErrMsg = 0;
|
||||
}
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
@@ -242,7 +240,7 @@ int sqlite3_blob_open(
|
||||
}
|
||||
}
|
||||
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
|
||||
assert( pBlob->pStmt || db->mallocFailed );
|
||||
if( pBlob->pStmt ){
|
||||
|
||||
@@ -278,7 +276,8 @@ int sqlite3_blob_open(
|
||||
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
|
||||
pTab->pSchema->schema_cookie,
|
||||
pTab->pSchema->iGeneration);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
||||
|
||||
/* Make sure a mutex is held on the table to be accessed */
|
||||
@@ -293,7 +292,7 @@ int sqlite3_blob_open(
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[0].p2 = pTab->tnum;
|
||||
aOp[0].p3 = wrFlag;
|
||||
sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
if( db->mallocFailed==0 ){
|
||||
#endif
|
||||
@@ -315,10 +314,10 @@ int sqlite3_blob_open(
|
||||
aOp[1].p4.i = pTab->nCol+1;
|
||||
aOp[3].p2 = pTab->nCol;
|
||||
|
||||
pParse->nVar = 0;
|
||||
pParse->nMem = 1;
|
||||
pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
sParse.nVar = 0;
|
||||
sParse.nMem = 1;
|
||||
sParse.nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, &sParse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +339,7 @@ blob_open_out:
|
||||
}
|
||||
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
sqlite3ParserReset(&sParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
||||
+220
-94
@@ -42,8 +42,7 @@ int sqlite3VdbeCheckMemInvariants(Mem *p){
|
||||
|
||||
if( p->flags & MEM_Null ){
|
||||
/* Cannot be both MEM_Null and some other type */
|
||||
assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob
|
||||
|MEM_RowSet|MEM_Frame|MEM_Agg|MEM_Zero))==0 );
|
||||
assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|MEM_Agg))==0 );
|
||||
|
||||
/* If MEM_Null is set, then either the value is a pure NULL (the usual
|
||||
** case) or it is a pointer set using sqlite3_bind_pointer() or
|
||||
@@ -93,6 +92,51 @@ int sqlite3VdbeCheckMemInvariants(Mem *p){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Check that string value of pMem agrees with its integer or real value.
|
||||
**
|
||||
** A single int or real value always converts to the same strings. But
|
||||
** many different strings can be converted into the same int or real.
|
||||
** If a table contains a numeric value and an index is based on the
|
||||
** corresponding string value, then it is important that the string be
|
||||
** derived from the numeric value, not the other way around, to ensure
|
||||
** that the index and table are consistent. See ticket
|
||||
** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for
|
||||
** an example.
|
||||
**
|
||||
** This routine looks at pMem to verify that if it has both a numeric
|
||||
** representation and a string representation then the string rep has
|
||||
** been derived from the numeric and not the other way around. It returns
|
||||
** true if everything is ok and false if there is a problem.
|
||||
**
|
||||
** This routine is for use inside of assert() statements only.
|
||||
*/
|
||||
int sqlite3VdbeMemConsistentDualRep(Mem *p){
|
||||
char zBuf[100];
|
||||
char *z;
|
||||
int i, j, incr;
|
||||
if( (p->flags & MEM_Str)==0 ) return 1;
|
||||
if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1;
|
||||
if( p->flags & MEM_Int ){
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r);
|
||||
}
|
||||
z = p->z;
|
||||
i = j = 0;
|
||||
incr = 1;
|
||||
if( p->enc!=SQLITE_UTF8 ){
|
||||
incr = 2;
|
||||
if( p->enc==SQLITE_UTF16BE ) z++;
|
||||
}
|
||||
while( zBuf[j] ){
|
||||
if( zBuf[j++]!=z[i] ) return 0;
|
||||
i += incr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** If pMem is an object with a valid string representation, this routine
|
||||
@@ -111,7 +155,7 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
int rc;
|
||||
#endif
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|
||||
|| desiredEnc==SQLITE_UTF16BE );
|
||||
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
|
||||
@@ -144,7 +188,7 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
|
||||
*/
|
||||
SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
|
||||
assert( sqlite3VdbeCheckMemInvariants(pMem) );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
testcase( pMem->db==0 );
|
||||
|
||||
/* If the bPreserve flag is set to true, then the memory cell must already
|
||||
@@ -155,7 +199,7 @@ SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
|
||||
assert( pMem->szMalloc==0
|
||||
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
|
||||
if( n<32 ) n = 32;
|
||||
if( bPreserve && pMem->szMalloc>0 && pMem->z==pMem->zMalloc ){
|
||||
if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
|
||||
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
|
||||
bPreserve = 0;
|
||||
}else{
|
||||
@@ -171,7 +215,8 @@ SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
|
||||
pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
|
||||
}
|
||||
|
||||
if( bPreserve && pMem->z && ALWAYS(pMem->z!=pMem->zMalloc) ){
|
||||
if( bPreserve && pMem->z ){
|
||||
assert( pMem->z!=pMem->zMalloc );
|
||||
memcpy(pMem->zMalloc, pMem->z, pMem->n);
|
||||
}
|
||||
if( (pMem->flags&MEM_Dyn)!=0 ){
|
||||
@@ -209,6 +254,20 @@ int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** It is already known that pMem contains an unterminated string.
|
||||
** Add the zero terminator.
|
||||
*/
|
||||
static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
|
||||
if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pMem->z[pMem->n] = 0;
|
||||
pMem->z[pMem->n+1] = 0;
|
||||
pMem->flags |= MEM_Term;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change pMem so that its MEM_Str or MEM_Blob value is stored in
|
||||
** MEM.zMalloc, where it can be safely written.
|
||||
@@ -217,16 +276,12 @@ int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
|
||||
*/
|
||||
int sqlite3VdbeMemMakeWriteable(Mem *pMem){
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
|
||||
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
|
||||
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
|
||||
if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pMem->z[pMem->n] = 0;
|
||||
pMem->z[pMem->n+1] = 0;
|
||||
pMem->flags |= MEM_Term;
|
||||
int rc = vdbeMemAddTerminator(pMem);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}
|
||||
pMem->flags &= ~MEM_Ephem;
|
||||
@@ -246,7 +301,7 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){
|
||||
int nByte;
|
||||
assert( pMem->flags & MEM_Zero );
|
||||
assert( pMem->flags&MEM_Blob );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
|
||||
/* Set nByte to the number of bytes required to store the expanded blob. */
|
||||
@@ -265,20 +320,6 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** It is already known that pMem contains an unterminated string.
|
||||
** Add the zero terminator.
|
||||
*/
|
||||
static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
|
||||
if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pMem->z[pMem->n] = 0;
|
||||
pMem->z[pMem->n+1] = 0;
|
||||
pMem->flags |= MEM_Term;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the given Mem is \u0000 terminated.
|
||||
*/
|
||||
@@ -315,7 +356,7 @@ int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
|
||||
assert( !(fg&MEM_Zero) );
|
||||
assert( !(fg&(MEM_Str|MEM_Blob)) );
|
||||
assert( fg&(MEM_Int|MEM_Real) );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
|
||||
@@ -353,28 +394,55 @@ int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
|
||||
** otherwise.
|
||||
*/
|
||||
int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
int rc = SQLITE_OK;
|
||||
if( ALWAYS(pFunc && pFunc->xFinalize) ){
|
||||
sqlite3_context ctx;
|
||||
Mem t;
|
||||
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.flags = MEM_Null;
|
||||
t.db = pMem->db;
|
||||
ctx.pOut = &t;
|
||||
ctx.pMem = pMem;
|
||||
ctx.pFunc = pFunc;
|
||||
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
|
||||
assert( (pMem->flags & MEM_Dyn)==0 );
|
||||
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
|
||||
memcpy(pMem, &t, sizeof(t));
|
||||
rc = ctx.isError;
|
||||
}
|
||||
return rc;
|
||||
sqlite3_context ctx;
|
||||
Mem t;
|
||||
assert( pFunc!=0 );
|
||||
assert( pFunc->xFinalize!=0 );
|
||||
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.flags = MEM_Null;
|
||||
t.db = pMem->db;
|
||||
ctx.pOut = &t;
|
||||
ctx.pMem = pMem;
|
||||
ctx.pFunc = pFunc;
|
||||
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
|
||||
assert( (pMem->flags & MEM_Dyn)==0 );
|
||||
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
|
||||
memcpy(pMem, &t, sizeof(t));
|
||||
return ctx.isError;
|
||||
}
|
||||
|
||||
/*
|
||||
** Memory cell pAccum contains the context of an aggregate function.
|
||||
** This routine calls the xValue method for that function and stores
|
||||
** the results in memory cell pMem.
|
||||
**
|
||||
** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK
|
||||
** otherwise.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
|
||||
sqlite3_context ctx;
|
||||
Mem t;
|
||||
assert( pFunc!=0 );
|
||||
assert( pFunc->xValue!=0 );
|
||||
assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef );
|
||||
assert( pAccum->db==0 || sqlite3_mutex_held(pAccum->db->mutex) );
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.flags = MEM_Null;
|
||||
t.db = pAccum->db;
|
||||
sqlite3VdbeMemSetNull(pOut);
|
||||
ctx.pOut = pOut;
|
||||
ctx.pMem = pAccum;
|
||||
ctx.pFunc = pFunc;
|
||||
pFunc->xValue(&ctx);
|
||||
return ctx.isError;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** If the memory cell contains a value that must be freed by
|
||||
** invoking the external callback in Mem.xDel, then this routine
|
||||
@@ -393,15 +461,8 @@ static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){
|
||||
testcase( p->flags & MEM_Dyn );
|
||||
}
|
||||
if( p->flags&MEM_Dyn ){
|
||||
assert( (p->flags&MEM_RowSet)==0 );
|
||||
assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
|
||||
p->xDel((void *)p->z);
|
||||
}else if( p->flags&MEM_RowSet ){
|
||||
sqlite3RowSetClear(p->u.pRowSet);
|
||||
}else if( p->flags&MEM_Frame ){
|
||||
VdbeFrame *pFrame = p->u.pFrame;
|
||||
pFrame->pParent = pFrame->v->pDelFrame;
|
||||
pFrame->v->pDelFrame = pFrame;
|
||||
}
|
||||
p->flags = MEM_Null;
|
||||
}
|
||||
@@ -532,6 +593,16 @@ double sqlite3VdbeRealValue(Mem *pMem){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return 1 if pMem represents true, and return 0 if pMem represents false.
|
||||
** Return the value ifNull if pMem is NULL.
|
||||
*/
|
||||
int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
|
||||
if( pMem->flags & MEM_Int ) return pMem->u.i!=0;
|
||||
if( pMem->flags & MEM_Null ) return ifNull;
|
||||
return sqlite3VdbeRealValue(pMem)!=0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The MEM structure is already a MEM_Real. Try to also make it a
|
||||
** MEM_Int if we can.
|
||||
@@ -539,7 +610,7 @@ double sqlite3VdbeRealValue(Mem *pMem){
|
||||
void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
||||
i64 ix;
|
||||
assert( pMem->flags & MEM_Real );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
@@ -566,7 +637,7 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
||||
*/
|
||||
int sqlite3VdbeMemIntegerify(Mem *pMem){
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
pMem->u.i = sqlite3VdbeIntValue(pMem);
|
||||
@@ -587,6 +658,18 @@ int sqlite3VdbeMemRealify(Mem *pMem){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Compare a floating point value to an integer. Return true if the two
|
||||
** values are the same within the precision of the floating point value.
|
||||
**
|
||||
** For some versions of GCC on 32-bit machines, if you do the more obvious
|
||||
** comparison of "r1==(double)i" you sometimes get an answer of false even
|
||||
** though the r1 and (double)i values are bit-for-bit the same.
|
||||
*/
|
||||
static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
|
||||
double r2 = (double)i;
|
||||
return memcmp(&r1, &r2, sizeof(r1))==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert pMem so that it has types MEM_Real or MEM_Int or both.
|
||||
** Invalidate any prior representations.
|
||||
@@ -597,14 +680,21 @@ int sqlite3VdbeMemRealify(Mem *pMem){
|
||||
*/
|
||||
int sqlite3VdbeMemNumerify(Mem *pMem){
|
||||
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
|
||||
int rc;
|
||||
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
|
||||
rc = sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc);
|
||||
if( rc==0 ){
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
}else{
|
||||
pMem->u.r = sqlite3VdbeRealValue(pMem);
|
||||
MemSetTypeFlag(pMem, MEM_Real);
|
||||
sqlite3VdbeIntegerAffinity(pMem);
|
||||
i64 i = pMem->u.i;
|
||||
sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
|
||||
if( rc==1 && sqlite3RealSameAsInt(pMem->u.r, i) ){
|
||||
pMem->u.i = i;
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
}else{
|
||||
MemSetTypeFlag(pMem, MEM_Real);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
|
||||
@@ -731,7 +821,7 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
|
||||
}
|
||||
|
||||
/* A no-op destructor */
|
||||
static void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
|
||||
void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
|
||||
|
||||
/*
|
||||
** Set the value stored in *pMem should already be a NULL.
|
||||
@@ -765,26 +855,36 @@ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Return true if the Mem holds a RowSet object. This routine is intended
|
||||
** for use inside of assert() statements.
|
||||
*/
|
||||
int sqlite3VdbeMemIsRowSet(const Mem *pMem){
|
||||
return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn)
|
||||
&& pMem->xDel==sqlite3RowSetDelete;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Delete any previous value and set the value of pMem to be an
|
||||
** empty boolean index.
|
||||
**
|
||||
** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation
|
||||
** error occurs.
|
||||
*/
|
||||
void sqlite3VdbeMemSetRowSet(Mem *pMem){
|
||||
int sqlite3VdbeMemSetRowSet(Mem *pMem){
|
||||
sqlite3 *db = pMem->db;
|
||||
RowSet *p;
|
||||
assert( db!=0 );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
sqlite3VdbeMemRelease(pMem);
|
||||
pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
|
||||
if( db->mallocFailed ){
|
||||
pMem->flags = MEM_Null;
|
||||
pMem->szMalloc = 0;
|
||||
}else{
|
||||
assert( pMem->zMalloc );
|
||||
pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
|
||||
pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
|
||||
assert( pMem->u.pRowSet!=0 );
|
||||
pMem->flags = MEM_RowSet;
|
||||
}
|
||||
p = sqlite3RowSetInit(db);
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
pMem->z = (char*)p;
|
||||
pMem->flags = MEM_Blob|MEM_Dyn;
|
||||
pMem->xDel = sqlite3RowSetDelete;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -817,7 +917,21 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
Mem *pX;
|
||||
for(i=0, pX=pVdbe->aMem; i<pVdbe->nMem; i++, pX++){
|
||||
if( pX->pScopyFrom==pMem ){
|
||||
pX->flags |= MEM_Undefined;
|
||||
/* If pX is marked as a shallow copy of pMem, then verify that
|
||||
** no significant changes have been made to pX since the OP_SCopy.
|
||||
** A significant change would indicated a missed call to this
|
||||
** function for pX. Minor changes, such as adding or removing a
|
||||
** dual type, are allowed, as long as the underlying value is the
|
||||
** same. */
|
||||
u16 mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
|
||||
assert( (mFlags&MEM_Int)==0 || pMem->u.i==pX->u.i );
|
||||
assert( (mFlags&MEM_Real)==0 || pMem->u.r==pX->u.r );
|
||||
assert( (mFlags&MEM_Str)==0 || (pMem->n==pX->n && pMem->z==pX->z) );
|
||||
assert( (mFlags&MEM_Blob)==0 || sqlite3BlobCompare(pMem,pX)==0 );
|
||||
|
||||
/* pMem is the register that is changing. But also mark pX as
|
||||
** undefined so that we can quickly detect the shallow-copy error */
|
||||
pX->flags = MEM_Undefined;
|
||||
pX->pScopyFrom = 0;
|
||||
}
|
||||
}
|
||||
@@ -838,7 +952,7 @@ static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){
|
||||
sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
|
||||
}
|
||||
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pFrom) );
|
||||
assert( pTo->db==pFrom->db );
|
||||
if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
@@ -856,7 +970,7 @@ void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
|
||||
int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pFrom) );
|
||||
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
pTo->flags &= ~MEM_Dyn;
|
||||
@@ -914,7 +1028,7 @@ int sqlite3VdbeMemSetStr(
|
||||
u16 flags = 0; /* New value for pMem->flags */
|
||||
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
|
||||
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
|
||||
if( !z ){
|
||||
@@ -931,7 +1045,7 @@ int sqlite3VdbeMemSetStr(
|
||||
if( nByte<0 ){
|
||||
assert( enc!=0 );
|
||||
if( enc==SQLITE_UTF8 ){
|
||||
nByte = sqlite3Strlen30(z);
|
||||
nByte = 0x7fffffff & (int)strlen(z);
|
||||
if( nByte>iLimit ) nByte = iLimit+1;
|
||||
}else{
|
||||
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
|
||||
@@ -1009,12 +1123,11 @@ static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
|
||||
){
|
||||
int rc;
|
||||
pMem->flags = MEM_Null;
|
||||
if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
|
||||
if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){
|
||||
rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z);
|
||||
if( rc==SQLITE_OK ){
|
||||
pMem->z[amt] = 0;
|
||||
pMem->z[amt+1] = 0;
|
||||
pMem->flags = MEM_Blob|MEM_Term;
|
||||
pMem->z[amt] = 0; /* Overrun area used when reading malformed records */
|
||||
pMem->flags = MEM_Blob;
|
||||
pMem->n = (int)amt;
|
||||
}else{
|
||||
sqlite3VdbeMemRelease(pMem);
|
||||
@@ -1037,7 +1150,7 @@ int sqlite3VdbeMemFromBtree(
|
||||
|
||||
/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
|
||||
** that both the BtShared and database handle mutexes are held. */
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
|
||||
assert( zData!=0 );
|
||||
|
||||
@@ -1061,7 +1174,7 @@ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
|
||||
assert( pVal!=0 );
|
||||
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
|
||||
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
|
||||
assert( (pVal->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pVal) );
|
||||
assert( (pVal->flags & (MEM_Null))==0 );
|
||||
if( pVal->flags & (MEM_Blob|MEM_Str) ){
|
||||
if( ExpandBlob(pVal) ) return 0;
|
||||
@@ -1083,6 +1196,7 @@ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
|
||||
assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
|
||||
|| pVal->db->mallocFailed );
|
||||
if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
|
||||
assert( sqlite3VdbeMemConsistentDualRep(pVal) );
|
||||
return pVal->z;
|
||||
}else{
|
||||
return 0;
|
||||
@@ -1103,8 +1217,9 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
|
||||
if( !pVal ) return 0;
|
||||
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
|
||||
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
|
||||
assert( (pVal->flags & MEM_RowSet)==0 );
|
||||
assert( !sqlite3VdbeMemIsRowSet(pVal) );
|
||||
if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
|
||||
assert( sqlite3VdbeMemConsistentDualRep(pVal) );
|
||||
return pVal->z;
|
||||
}
|
||||
if( pVal->flags&MEM_Null ){
|
||||
@@ -1163,7 +1278,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
|
||||
if( pRec ){
|
||||
pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
|
||||
if( pRec->pKeyInfo ){
|
||||
assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
|
||||
assert( pRec->pKeyInfo->nAllField==nCol );
|
||||
assert( pRec->pKeyInfo->enc==ENC(db) );
|
||||
pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
|
||||
for(i=0; i<nCol; i++){
|
||||
@@ -1320,7 +1435,11 @@ static int valueFromExpr(
|
||||
|
||||
assert( pExpr!=0 );
|
||||
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
|
||||
#if defined(SQLITE_ENABLE_STAT3_OR_STAT4)
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
#else
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
#endif
|
||||
|
||||
/* Compressed expressions only appear when parsing the DEFAULT clause
|
||||
** on a table column definition, and hence only when pCtx==0. This
|
||||
@@ -1404,18 +1523,25 @@ static int valueFromExpr(
|
||||
0, SQLITE_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
else if( op==TK_FUNCTION && pCtx!=0 ){
|
||||
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
|
||||
}
|
||||
#endif
|
||||
else if( op==TK_TRUEFALSE ){
|
||||
pVal = valueNew(db, pCtx);
|
||||
pVal->flags = MEM_Int;
|
||||
pVal->u.i = pExpr->u.zToken[4]==0;
|
||||
}
|
||||
|
||||
*ppVal = pVal;
|
||||
return rc;
|
||||
|
||||
no_mem:
|
||||
sqlite3OomFault(db);
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( pCtx==0 || pCtx->pParse->nErr==0 )
|
||||
#endif
|
||||
sqlite3OomFault(db);
|
||||
sqlite3DbFree(db, zVal);
|
||||
assert( *ppVal==0 );
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
@@ -1658,11 +1784,11 @@ int sqlite3Stat4Column(
|
||||
int iCol, /* Column to extract */
|
||||
sqlite3_value **ppVal /* OUT: Extracted value */
|
||||
){
|
||||
u32 t; /* a column type code */
|
||||
u32 t = 0; /* a column type code */
|
||||
int nHdr; /* Size of the header in the record */
|
||||
int iHdr; /* Next unread header byte */
|
||||
int iField; /* Next unread data byte */
|
||||
int szField; /* Size of the current data field */
|
||||
int szField = 0; /* Size of the current data field */
|
||||
int i; /* Column index */
|
||||
u8 *a = (u8*)pRec; /* Typecast byte array */
|
||||
Mem *pMem = *ppVal; /* Write result into this Mem object */
|
||||
@@ -1699,7 +1825,7 @@ int sqlite3Stat4Column(
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
|
||||
if( pRec ){
|
||||
int i;
|
||||
int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
|
||||
int nCol = pRec->pKeyInfo->nAllField;
|
||||
Mem *aMem = pRec->aMem;
|
||||
sqlite3 *db = aMem[0].db;
|
||||
for(i=0; i<nCol; i++){
|
||||
|
||||
+16
-14
@@ -823,7 +823,7 @@ static int vdbeSorterCompareText(
|
||||
}
|
||||
|
||||
if( res==0 ){
|
||||
if( pTask->pSorter->pKeyInfo->nField>1 ){
|
||||
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
||||
res = vdbeSorterCompareTail(
|
||||
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
||||
);
|
||||
@@ -892,7 +892,7 @@ static int vdbeSorterCompareInt(
|
||||
}
|
||||
|
||||
if( res==0 ){
|
||||
if( pTask->pSorter->pKeyInfo->nField>1 ){
|
||||
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
||||
res = vdbeSorterCompareTail(
|
||||
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
||||
);
|
||||
@@ -907,7 +907,7 @@ static int vdbeSorterCompareInt(
|
||||
/*
|
||||
** Initialize the temporary index cursor just opened as a sorter cursor.
|
||||
**
|
||||
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
|
||||
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField)
|
||||
** to determine the number of fields that should be compared from the
|
||||
** records being sorted. However, if the value passed as argument nField
|
||||
** is non-zero and the sorter is able to guarantee a stable sort, nField
|
||||
@@ -960,7 +960,7 @@ int sqlite3VdbeSorterInit(
|
||||
|
||||
assert( pCsr->pKeyInfo && pCsr->pBtx==0 );
|
||||
assert( pCsr->eCurType==CURTYPE_SORTER );
|
||||
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
|
||||
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
|
||||
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
|
||||
|
||||
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
|
||||
@@ -972,8 +972,7 @@ int sqlite3VdbeSorterInit(
|
||||
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
|
||||
pKeyInfo->db = 0;
|
||||
if( nField && nWorker==0 ){
|
||||
pKeyInfo->nXField += (pKeyInfo->nField - nField);
|
||||
pKeyInfo->nField = nField;
|
||||
pKeyInfo->nKeyField = nField;
|
||||
}
|
||||
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
|
||||
pSorter->nTask = nWorker + 1;
|
||||
@@ -1001,11 +1000,9 @@ int sqlite3VdbeSorterInit(
|
||||
mxCache = MIN(mxCache, SQLITE_MAX_PMASZ);
|
||||
pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache);
|
||||
|
||||
/* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
|
||||
** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
|
||||
** large heap allocations.
|
||||
*/
|
||||
if( sqlite3GlobalConfig.pScratch==0 ){
|
||||
/* Avoid large memory allocations if the application has requested
|
||||
** SQLITE_CONFIG_SMALL_MALLOC. */
|
||||
if( sqlite3GlobalConfig.bSmallMalloc==0 ){
|
||||
assert( pSorter->iMemory==0 );
|
||||
pSorter->nMemory = pgsz;
|
||||
pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz);
|
||||
@@ -1013,7 +1010,7 @@ int sqlite3VdbeSorterInit(
|
||||
}
|
||||
}
|
||||
|
||||
if( (pKeyInfo->nField+pKeyInfo->nXField)<13
|
||||
if( pKeyInfo->nAllField<13
|
||||
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
|
||||
){
|
||||
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
|
||||
@@ -1328,7 +1325,7 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){
|
||||
if( pTask->pUnpacked==0 ){
|
||||
pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo);
|
||||
if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
|
||||
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField;
|
||||
pTask->pUnpacked->errCode = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@@ -2110,7 +2107,11 @@ static int vdbeMergeEngineInit(
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int i; /* For looping over PmaReader objects */
|
||||
int nTree = pMerger->nTree;
|
||||
int nTree; /* Number of subtrees to merge */
|
||||
|
||||
/* Failure to allocate the merge would have been detected prior to
|
||||
** invoking this routine */
|
||||
assert( pMerger!=0 );
|
||||
|
||||
/* eMode is always INCRINIT_NORMAL in single-threaded mode */
|
||||
assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL );
|
||||
@@ -2119,6 +2120,7 @@ static int vdbeMergeEngineInit(
|
||||
assert( pMerger->pTask==0 );
|
||||
pMerger->pTask = pTask;
|
||||
|
||||
nTree = pMerger->nTree;
|
||||
for(i=0; i<nTree; i++){
|
||||
if( SQLITE_MAX_WORKER_THREADS>0 && eMode==INCRINIT_ROOT ){
|
||||
/* PmaReaders should be normally initialized in order, as if they are
|
||||
|
||||
+17
-17
@@ -82,7 +82,7 @@ char *sqlite3VdbeExpandSql(
|
||||
Mem *pVar; /* Value of a host parameter */
|
||||
StrAccum out; /* Accumulate the output here */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
Mem utf8; /* Used to convert UTF16 parameters into UTF8 for display */
|
||||
Mem utf8; /* Used to convert UTF16 into UTF8 for display */
|
||||
#endif
|
||||
char zBase[100]; /* Initial working space */
|
||||
|
||||
@@ -93,17 +93,17 @@ char *sqlite3VdbeExpandSql(
|
||||
while( *zRawSql ){
|
||||
const char *zStart = zRawSql;
|
||||
while( *(zRawSql++)!='\n' && *zRawSql );
|
||||
sqlite3StrAccumAppend(&out, "-- ", 3);
|
||||
sqlite3_str_append(&out, "-- ", 3);
|
||||
assert( (zRawSql - zStart) > 0 );
|
||||
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
|
||||
sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart));
|
||||
}
|
||||
}else if( p->nVar==0 ){
|
||||
sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
||||
sqlite3_str_append(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
||||
}else{
|
||||
while( zRawSql[0] ){
|
||||
n = findNextHostParameter(zRawSql, &nToken);
|
||||
assert( n>0 );
|
||||
sqlite3StrAccumAppend(&out, zRawSql, n);
|
||||
sqlite3_str_append(&out, zRawSql, n);
|
||||
zRawSql += n;
|
||||
assert( zRawSql[0] || nToken==0 );
|
||||
if( nToken==0 ) break;
|
||||
@@ -129,11 +129,11 @@ char *sqlite3VdbeExpandSql(
|
||||
assert( idx>0 && idx<=p->nVar );
|
||||
pVar = &p->aVar[idx-1];
|
||||
if( pVar->flags & MEM_Null ){
|
||||
sqlite3StrAccumAppend(&out, "NULL", 4);
|
||||
sqlite3_str_append(&out, "NULL", 4);
|
||||
}else if( pVar->flags & MEM_Int ){
|
||||
sqlite3XPrintf(&out, "%lld", pVar->u.i);
|
||||
sqlite3_str_appendf(&out, "%lld", pVar->u.i);
|
||||
}else if( pVar->flags & MEM_Real ){
|
||||
sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
|
||||
sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
|
||||
}else if( pVar->flags & MEM_Str ){
|
||||
int nOut; /* Number of bytes of the string text to include in output */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@@ -143,7 +143,7 @@ char *sqlite3VdbeExpandSql(
|
||||
utf8.db = db;
|
||||
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
||||
if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
|
||||
out.accError = STRACCUM_NOMEM;
|
||||
out.accError = SQLITE_NOMEM;
|
||||
out.nAlloc = 0;
|
||||
}
|
||||
pVar = &utf8;
|
||||
@@ -156,38 +156,38 @@ char *sqlite3VdbeExpandSql(
|
||||
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
|
||||
}
|
||||
#endif
|
||||
sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
|
||||
sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z);
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut<pVar->n ){
|
||||
sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
|
||||
#endif
|
||||
}else if( pVar->flags & MEM_Zero ){
|
||||
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
}else{
|
||||
int nOut; /* Number of bytes of the blob to include in output */
|
||||
assert( pVar->flags & MEM_Blob );
|
||||
sqlite3StrAccumAppend(&out, "x'", 2);
|
||||
sqlite3_str_append(&out, "x'", 2);
|
||||
nOut = pVar->n;
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
|
||||
#endif
|
||||
for(i=0; i<nOut; i++){
|
||||
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
|
||||
sqlite3_str_appendf(&out, "%02x", pVar->z[i]&0xff);
|
||||
}
|
||||
sqlite3StrAccumAppend(&out, "'", 1);
|
||||
sqlite3_str_append(&out, "'", 1);
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut<pVar->n ){
|
||||
sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if( out.accError ) sqlite3StrAccumReset(&out);
|
||||
if( out.accError ) sqlite3_str_reset(&out);
|
||||
return sqlite3StrAccumFinish(&out);
|
||||
}
|
||||
|
||||
|
||||
+69
-62
@@ -42,8 +42,10 @@ Module *sqlite3VtabCreateModule(
|
||||
){
|
||||
Module *pMod;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1);
|
||||
if( pMod ){
|
||||
pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
|
||||
if( pMod==0 ){
|
||||
sqlite3OomFault(db);
|
||||
}else{
|
||||
Module *pDel;
|
||||
char *zCopy = (char *)(&pMod[1]);
|
||||
memcpy(zCopy, zName, nName+1);
|
||||
@@ -260,7 +262,7 @@ void sqlite3VtabUnlockList(sqlite3 *db){
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
if( p ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
do {
|
||||
VTable *pNext = p->pNext;
|
||||
sqlite3VtabUnlock(p);
|
||||
@@ -518,13 +520,14 @@ static int vtabCallConstructor(
|
||||
}
|
||||
}
|
||||
|
||||
zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
|
||||
zModuleName = sqlite3DbStrDup(db, pTab->zName);
|
||||
if( !zModuleName ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
pVTable = sqlite3DbMallocZero(db, sizeof(VTable));
|
||||
pVTable = sqlite3MallocZero(sizeof(VTable));
|
||||
if( !pVTable ){
|
||||
sqlite3OomFault(db);
|
||||
sqlite3DbFree(db, zModuleName);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
@@ -644,6 +647,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "%s", zErr);
|
||||
pParse->rc = rc;
|
||||
}
|
||||
sqlite3DbFree(db, zErr);
|
||||
}
|
||||
@@ -733,10 +737,10 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
|
||||
*/
|
||||
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
VtabCtx *pCtx;
|
||||
Parse *pParse;
|
||||
int rc = SQLITE_OK;
|
||||
Table *pTab;
|
||||
char *zErr = 0;
|
||||
Parse sParse;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
|
||||
@@ -753,55 +757,55 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
pTab = pCtx->pTab;
|
||||
assert( IsVirtual(pTab) );
|
||||
|
||||
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pParse->declareVtab = 1;
|
||||
pParse->db = db;
|
||||
pParse->nQueryLoop = 1;
|
||||
|
||||
if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr)
|
||||
&& pParse->pNewTable
|
||||
&& !db->mallocFailed
|
||||
&& !pParse->pNewTable->pSelect
|
||||
&& !IsVirtual(pParse->pNewTable)
|
||||
){
|
||||
if( !pTab->aCol ){
|
||||
Table *pNew = pParse->pNewTable;
|
||||
Index *pIdx;
|
||||
pTab->aCol = pNew->aCol;
|
||||
pTab->nCol = pNew->nCol;
|
||||
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
||||
pNew->nCol = 0;
|
||||
pNew->aCol = 0;
|
||||
assert( pTab->pIndex==0 );
|
||||
if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pIdx = pNew->pIndex;
|
||||
if( pIdx ){
|
||||
assert( pIdx->pNext==0 );
|
||||
pTab->pIndex = pIdx;
|
||||
pNew->pIndex = 0;
|
||||
pIdx->pTable = pTab;
|
||||
}
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
|
||||
sParse.db = db;
|
||||
sParse.nQueryLoop = 1;
|
||||
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
|
||||
&& sParse.pNewTable
|
||||
&& !db->mallocFailed
|
||||
&& !sParse.pNewTable->pSelect
|
||||
&& !IsVirtual(sParse.pNewTable)
|
||||
){
|
||||
if( !pTab->aCol ){
|
||||
Table *pNew = sParse.pNewTable;
|
||||
Index *pIdx;
|
||||
pTab->aCol = pNew->aCol;
|
||||
pTab->nCol = pNew->nCol;
|
||||
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
||||
pNew->nCol = 0;
|
||||
pNew->aCol = 0;
|
||||
assert( pTab->pIndex==0 );
|
||||
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
|
||||
if( !HasRowid(pNew)
|
||||
&& pCtx->pVTable->pMod->pModule->xUpdate!=0
|
||||
&& sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1
|
||||
){
|
||||
/* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0)
|
||||
** or else must have a single-column PRIMARY KEY */
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pIdx = pNew->pIndex;
|
||||
if( pIdx ){
|
||||
assert( pIdx->pNext==0 );
|
||||
pTab->pIndex = pIdx;
|
||||
pNew->pIndex = 0;
|
||||
pIdx->pTable = pTab;
|
||||
}
|
||||
pCtx->bDeclared = 1;
|
||||
}else{
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pParse->declareVtab = 0;
|
||||
|
||||
if( pParse->pVdbe ){
|
||||
sqlite3VdbeFinalize(pParse->pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(db, pParse->pNewTable);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
pCtx->bDeclared = 1;
|
||||
}else{
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sParse.eParseMode = PARSE_MODE_NORMAL;
|
||||
|
||||
if( sParse.pVdbe ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(db, sParse.pNewTable);
|
||||
sqlite3ParserReset(&sParse);
|
||||
|
||||
assert( (rc&0xff)==rc );
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
@@ -1045,9 +1049,6 @@ FuncDef *sqlite3VtabOverloadFunction(
|
||||
void *pArg = 0;
|
||||
FuncDef *pNew;
|
||||
int rc = 0;
|
||||
char *zLowerName;
|
||||
unsigned char *z;
|
||||
|
||||
|
||||
/* Check to see the left operand is a column in a virtual table */
|
||||
if( NEVER(pExpr==0) ) return pDef;
|
||||
@@ -1062,16 +1063,22 @@ FuncDef *sqlite3VtabOverloadFunction(
|
||||
if( pMod->xFindFunction==0 ) return pDef;
|
||||
|
||||
/* Call the xFindFunction method on the virtual table implementation
|
||||
** to see if the implementation wants to overload this function
|
||||
** to see if the implementation wants to overload this function.
|
||||
**
|
||||
** Though undocumented, we have historically always invoked xFindFunction
|
||||
** with an all lower-case function name. Continue in this tradition to
|
||||
** avoid any chance of an incompatibility.
|
||||
*/
|
||||
zLowerName = sqlite3DbStrDup(db, pDef->zName);
|
||||
if( zLowerName ){
|
||||
for(z=(unsigned char*)zLowerName; *z; z++){
|
||||
*z = sqlite3UpperToLower[*z];
|
||||
#ifdef SQLITE_DEBUG
|
||||
{
|
||||
int i;
|
||||
for(i=0; pDef->zName[i]; i++){
|
||||
unsigned char x = (unsigned char)pDef->zName[i];
|
||||
assert( x==sqlite3UpperToLower[x] );
|
||||
}
|
||||
rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
|
||||
sqlite3DbFree(db, zLowerName);
|
||||
}
|
||||
#endif
|
||||
rc = pMod->xFindFunction(pVtab, nArg, pDef->zName, &xSFunc, &pArg);
|
||||
if( rc==0 ){
|
||||
return pDef;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Additional values that can be added to the sync_flags argument of
|
||||
** sqlite3WalFrames():
|
||||
/* Macros for extracting appropriate sync flags for either transaction
|
||||
** commits (WAL_SYNC_FLAGS(X)) or for checkpoint ops (CKPT_SYNC_FLAGS(X)):
|
||||
*/
|
||||
#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
|
||||
#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
|
||||
#define WAL_SYNC_FLAGS(X) ((X)&0x03)
|
||||
#define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03)
|
||||
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
# define sqlite3WalOpen(x,y,z) 0
|
||||
@@ -132,6 +132,8 @@ int sqlite3WalHeapMemory(Wal *pWal);
|
||||
int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
|
||||
void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
|
||||
int sqlite3WalSnapshotRecover(Wal *pWal);
|
||||
int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
|
||||
void sqlite3WalSnapshotUnlock(Wal *pWal);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
|
||||
+32
-22
@@ -40,18 +40,30 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
|
||||
int rc;
|
||||
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
|
||||
testcase( ExprHasProperty(pExpr, EP_Reduced) );
|
||||
rc = pWalker->xExprCallback(pWalker, pExpr);
|
||||
if( rc ) return rc & WRC_Abort;
|
||||
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
|
||||
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
assert( pExpr->x.pList==0 || pExpr->pRight==0 );
|
||||
if( pExpr->pRight ){
|
||||
if( walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
||||
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else if( pExpr->x.pList ){
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
while(1){
|
||||
rc = pWalker->xExprCallback(pWalker, pExpr);
|
||||
if( rc ) return rc & WRC_Abort;
|
||||
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
|
||||
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
assert( pExpr->x.pList==0 || pExpr->pRight==0 );
|
||||
if( pExpr->pRight ){
|
||||
pExpr = pExpr->pRight;
|
||||
continue;
|
||||
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else if( pExpr->x.pList ){
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( !ExprHasProperty(pExpr, EP_Reduced) && pExpr->pWin ){
|
||||
Window *pWin = pExpr->pWin;
|
||||
if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
|
||||
if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
@@ -87,7 +99,6 @@ int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
|
||||
if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort;
|
||||
if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, p->pOffset) ) return WRC_Abort;
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
@@ -104,16 +115,15 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
|
||||
struct SrcList_item *pItem;
|
||||
|
||||
pSrc = p->pSrc;
|
||||
if( ALWAYS(pSrc) ){
|
||||
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
||||
if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
if( pItem->fg.isTabFunc
|
||||
&& sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
|
||||
){
|
||||
return WRC_Abort;
|
||||
}
|
||||
assert( pSrc!=0 );
|
||||
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
||||
if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
if( pItem->fg.isTabFunc
|
||||
&& sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
|
||||
){
|
||||
return WRC_Abort;
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
|
||||
+411
-183
File diff suppressed because it is too large
Load Diff
+8
-6
@@ -19,7 +19,7 @@
|
||||
** Trace output macros
|
||||
*/
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
/***/ int sqlite3WhereTrace;
|
||||
/***/ extern int sqlite3WhereTrace;
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) \
|
||||
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
|
||||
@@ -82,6 +82,8 @@ struct WhereLevel {
|
||||
struct InLoop {
|
||||
int iCur; /* The VDBE cursor used by this IN operator */
|
||||
int addrInTop; /* Top of the IN loop */
|
||||
int iBase; /* Base register of multi-key index record */
|
||||
int nPrefix; /* Number of prior entires in the key */
|
||||
u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
|
||||
} *aInLoop; /* Information about each nested IN operator */
|
||||
} in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
|
||||
@@ -320,6 +322,7 @@ struct WhereClause {
|
||||
WhereInfo *pWInfo; /* WHERE clause processing context */
|
||||
WhereClause *pOuter; /* Outer conjunction */
|
||||
u8 op; /* Split operator. TK_AND or TK_OR */
|
||||
u8 hasOr; /* True if any a[].eOperator is WO_OR */
|
||||
int nTerm; /* Number of terms */
|
||||
int nSlot; /* Number of entries in a[] */
|
||||
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
|
||||
@@ -467,12 +470,10 @@ int sqlite3WhereExplainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
int iLevel, /* Value for "level" column of output */
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
);
|
||||
#else
|
||||
# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
|
||||
# define sqlite3WhereExplainOneScan(u,v,w,x) 0
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
void sqlite3WhereAddScanStatus(
|
||||
@@ -495,6 +496,7 @@ void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
|
||||
void sqlite3WhereClauseClear(WhereClause*);
|
||||
void sqlite3WhereSplit(WhereClause*,Expr*,u8);
|
||||
Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
|
||||
Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
|
||||
Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
|
||||
void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
|
||||
void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
|
||||
@@ -515,7 +517,6 @@ void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
|
||||
** WO_LE == SQLITE_INDEX_CONSTRAINT_LE
|
||||
** WO_GT == SQLITE_INDEX_CONSTRAINT_GT
|
||||
** WO_GE == SQLITE_INDEX_CONSTRAINT_GE
|
||||
** WO_MATCH == SQLITE_INDEX_CONSTRAINT_MATCH
|
||||
*/
|
||||
#define WO_IN 0x0001
|
||||
#define WO_EQ 0x0002
|
||||
@@ -523,7 +524,7 @@ void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
|
||||
#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
|
||||
#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
|
||||
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
|
||||
#define WO_MATCH 0x0040
|
||||
#define WO_AUX 0x0040 /* Op useful to virtual tables only */
|
||||
#define WO_IS 0x0080
|
||||
#define WO_ISNULL 0x0100
|
||||
#define WO_OR 0x0200 /* Two or more OR-connected terms */
|
||||
@@ -558,3 +559,4 @@ void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
|
||||
#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
|
||||
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
|
||||
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
|
||||
#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
|
||||
|
||||
+200
-119
@@ -51,23 +51,23 @@ static void explainAppendTerm(
|
||||
int i;
|
||||
|
||||
assert( nTerm>=1 );
|
||||
if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
if( bAnd ) sqlite3_str_append(pStr, " AND ", 5);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
||||
if( i ) sqlite3_str_append(pStr, ",", 1);
|
||||
sqlite3_str_appendall(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
||||
|
||||
sqlite3StrAccumAppend(pStr, zOp, 1);
|
||||
sqlite3_str_append(pStr, zOp, 1);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppend(pStr, "?", 1);
|
||||
if( i ) sqlite3_str_append(pStr, ",", 1);
|
||||
sqlite3_str_append(pStr, "?", 1);
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -91,11 +91,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
int i, j;
|
||||
|
||||
if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
|
||||
sqlite3StrAccumAppend(pStr, " (", 2);
|
||||
sqlite3_str_append(pStr, " (", 2);
|
||||
for(i=0; i<nEq; i++){
|
||||
const char *z = explainIndexColumnName(pIndex, i);
|
||||
if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
||||
if( i ) sqlite3_str_append(pStr, " AND ", 5);
|
||||
sqlite3_str_appendf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
||||
}
|
||||
|
||||
j = i;
|
||||
@@ -106,7 +106,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
|
||||
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
sqlite3_str_append(pStr, ")", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -122,19 +122,16 @@ int sqlite3WhereExplainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
int iLevel, /* Value for "level" column of output */
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
int ret = 0;
|
||||
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
||||
if( pParse->explain==2 )
|
||||
if( sqlite3ParseToplevel(pParse)->explain==2 )
|
||||
#endif
|
||||
{
|
||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
||||
u32 flags; /* Flags that describe this loop */
|
||||
@@ -151,15 +148,15 @@ int sqlite3WhereExplainOneScan(
|
||||
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
||||
|
||||
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
if( pItem->pSelect ){
|
||||
sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
|
||||
sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId);
|
||||
}else{
|
||||
sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
|
||||
sqlite3_str_appendf(&str, " TABLE %s", pItem->zName);
|
||||
}
|
||||
|
||||
if( pItem->zAlias ){
|
||||
sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
|
||||
sqlite3_str_appendf(&str, " AS %s", pItem->zAlias);
|
||||
}
|
||||
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||
const char *zFmt = 0;
|
||||
@@ -182,8 +179,8 @@ int sqlite3WhereExplainOneScan(
|
||||
zFmt = "INDEX %s";
|
||||
}
|
||||
if( zFmt ){
|
||||
sqlite3StrAccumAppend(&str, " USING ", 7);
|
||||
sqlite3XPrintf(&str, zFmt, pIdx->zName);
|
||||
sqlite3_str_append(&str, " USING ", 7);
|
||||
sqlite3_str_appendf(&str, zFmt, pIdx->zName);
|
||||
explainIndexRange(&str, pLoop);
|
||||
}
|
||||
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
|
||||
@@ -198,23 +195,26 @@ int sqlite3WhereExplainOneScan(
|
||||
assert( flags&WHERE_TOP_LIMIT);
|
||||
zRangeOp = "<";
|
||||
}
|
||||
sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
||||
sqlite3_str_appendf(&str,
|
||||
" USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
|
||||
sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
||||
sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
||||
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
||||
if( pLoop->nOut>=10 ){
|
||||
sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
|
||||
sqlite3_str_appendf(&str, " (~%llu rows)",
|
||||
sqlite3LogEstToInt(pLoop->nOut));
|
||||
}else{
|
||||
sqlite3StrAccumAppend(&str, " (~1 row)", 9);
|
||||
sqlite3_str_append(&str, " (~1 row)", 9);
|
||||
}
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
|
||||
pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -294,8 +294,8 @@ void sqlite3WhereAddScanStatus(
|
||||
*/
|
||||
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
||||
int nLoop = 0;
|
||||
while( ALWAYS(pTerm!=0)
|
||||
&& (pTerm->wtFlags & TERM_CODED)==0
|
||||
assert( pTerm!=0 );
|
||||
while( (pTerm->wtFlags & TERM_CODED)==0
|
||||
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
|
||||
&& (pLevel->notReady & pTerm->prereqAll)==0
|
||||
){
|
||||
@@ -306,6 +306,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
||||
}
|
||||
if( pTerm->iParent<0 ) break;
|
||||
pTerm = &pTerm->pWC->a[pTerm->iParent];
|
||||
assert( pTerm!=0 );
|
||||
pTerm->nChild--;
|
||||
if( pTerm->nChild!=0 ) break;
|
||||
nLoop++;
|
||||
@@ -346,7 +347,6 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
|
||||
/* Code the OP_Affinity opcode if there is anything left to do. */
|
||||
if( n>0 ){
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, n);
|
||||
sqlite3ExprCacheAffinityChange(pParse, base, n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +376,102 @@ static void updateRangeAffinityStr(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** pX is an expression of the form: (vector) IN (SELECT ...)
|
||||
** In other words, it is a vector IN operator with a SELECT clause on the
|
||||
** LHS. But not all terms in the vector are indexable and the terms might
|
||||
** not be in the correct order for indexing.
|
||||
**
|
||||
** This routine makes a copy of the input pX expression and then adjusts
|
||||
** the vector on the LHS with corresponding changes to the SELECT so that
|
||||
** the vector contains only index terms and those terms are in the correct
|
||||
** order. The modified IN expression is returned. The caller is responsible
|
||||
** for deleting the returned expression.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** CREATE TABLE t1(a,b,c,d,e,f);
|
||||
** CREATE INDEX t1x1 ON t1(e,c);
|
||||
** SELECT * FROM t1 WHERE (a,b,c,d,e) IN (SELECT v,w,x,y,z FROM t2)
|
||||
** \_______________________________________/
|
||||
** The pX expression
|
||||
**
|
||||
** Since only columns e and c can be used with the index, in that order,
|
||||
** the modified IN expression that is returned will be:
|
||||
**
|
||||
** (e,c) IN (SELECT z,x FROM t2)
|
||||
**
|
||||
** The reduced pX is different from the original (obviously) and thus is
|
||||
** only used for indexing, to improve performance. The original unaltered
|
||||
** IN expression must also be run on each output row for correctness.
|
||||
*/
|
||||
static Expr *removeUnindexableInClauseTerms(
|
||||
Parse *pParse, /* The parsing context */
|
||||
int iEq, /* Look at loop terms starting here */
|
||||
WhereLoop *pLoop, /* The current loop */
|
||||
Expr *pX /* The IN expression to be reduced */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pNew = sqlite3ExprDup(db, pX, 0);
|
||||
if( db->mallocFailed==0 ){
|
||||
ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */
|
||||
ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */
|
||||
ExprList *pRhs = 0; /* New RHS after modifications */
|
||||
ExprList *pLhs = 0; /* New LHS after mods */
|
||||
int i; /* Loop counter */
|
||||
Select *pSelect; /* Pointer to the SELECT on the RHS */
|
||||
|
||||
for(i=iEq; i<pLoop->nLTerm; i++){
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ){
|
||||
int iField = pLoop->aLTerm[i]->iField - 1;
|
||||
assert( pOrigRhs->a[iField].pExpr!=0 );
|
||||
pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
|
||||
pOrigRhs->a[iField].pExpr = 0;
|
||||
assert( pOrigLhs->a[iField].pExpr!=0 );
|
||||
pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr);
|
||||
pOrigLhs->a[iField].pExpr = 0;
|
||||
}
|
||||
}
|
||||
sqlite3ExprListDelete(db, pOrigRhs);
|
||||
sqlite3ExprListDelete(db, pOrigLhs);
|
||||
pNew->pLeft->x.pList = pLhs;
|
||||
pNew->x.pSelect->pEList = pRhs;
|
||||
if( pLhs && pLhs->nExpr==1 ){
|
||||
/* Take care here not to generate a TK_VECTOR containing only a
|
||||
** single value. Since the parser never creates such a vector, some
|
||||
** of the subroutines do not handle this case. */
|
||||
Expr *p = pLhs->a[0].pExpr;
|
||||
pLhs->a[0].pExpr = 0;
|
||||
sqlite3ExprDelete(db, pNew->pLeft);
|
||||
pNew->pLeft = p;
|
||||
}
|
||||
pSelect = pNew->x.pSelect;
|
||||
if( pSelect->pOrderBy ){
|
||||
/* If the SELECT statement has an ORDER BY clause, zero the
|
||||
** iOrderByCol variables. These are set to non-zero when an
|
||||
** ORDER BY term exactly matches one of the terms of the
|
||||
** result-set. Since the result-set of the SELECT statement may
|
||||
** have been modified or reordered, these variables are no longer
|
||||
** set correctly. Since setting them is just an optimization,
|
||||
** it's easiest just to zero them here. */
|
||||
ExprList *pOrderBy = pSelect->pOrderBy;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
pOrderBy->a[i].u.x.iOrderByCol = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("For indexing, change the IN expr:\n");
|
||||
sqlite3TreeViewExpr(0, pX, 0);
|
||||
printf("Into:\n");
|
||||
sqlite3TreeViewExpr(0, pNew, 0);
|
||||
#endif
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for a single equality term of the WHERE clause. An equality
|
||||
** term can be either X=expr or X IN (...). pTerm is the term to be
|
||||
@@ -438,68 +534,23 @@ static int codeEqualityTerm(
|
||||
}
|
||||
}
|
||||
for(i=iEq;i<pLoop->nLTerm; i++){
|
||||
if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
|
||||
assert( pLoop->aLTerm[i]!=0 );
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ) nEq++;
|
||||
}
|
||||
|
||||
if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
|
||||
}else{
|
||||
Select *pSelect = pX->x.pSelect;
|
||||
sqlite3 *db = pParse->db;
|
||||
u16 savedDbOptFlags = db->dbOptFlags;
|
||||
ExprList *pOrigRhs = pSelect->pEList;
|
||||
ExprList *pOrigLhs = pX->pLeft->x.pList;
|
||||
ExprList *pRhs = 0; /* New Select.pEList for RHS */
|
||||
ExprList *pLhs = 0; /* New pX->pLeft vector */
|
||||
pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
|
||||
|
||||
for(i=iEq;i<pLoop->nLTerm; i++){
|
||||
if( pLoop->aLTerm[i]->pExpr==pX ){
|
||||
int iField = pLoop->aLTerm[i]->iField - 1;
|
||||
Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
|
||||
Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
|
||||
|
||||
pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
|
||||
pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
|
||||
}
|
||||
}
|
||||
if( !db->mallocFailed ){
|
||||
Expr *pLeft = pX->pLeft;
|
||||
|
||||
if( pSelect->pOrderBy ){
|
||||
/* If the SELECT statement has an ORDER BY clause, zero the
|
||||
** iOrderByCol variables. These are set to non-zero when an
|
||||
** ORDER BY term exactly matches one of the terms of the
|
||||
** result-set. Since the result-set of the SELECT statement may
|
||||
** have been modified or reordered, these variables are no longer
|
||||
** set correctly. Since setting them is just an optimization,
|
||||
** it's easiest just to zero them here. */
|
||||
ExprList *pOrderBy = pSelect->pOrderBy;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
pOrderBy->a[i].u.x.iOrderByCol = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take care here not to generate a TK_VECTOR containing only a
|
||||
** single value. Since the parser never creates such a vector, some
|
||||
** of the subroutines do not handle this case. */
|
||||
if( pLhs->nExpr==1 ){
|
||||
pX->pLeft = pLhs->a[0].pExpr;
|
||||
}else{
|
||||
pLeft->x.pList = pLhs;
|
||||
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
|
||||
testcase( aiMap==0 );
|
||||
}
|
||||
pSelect->pEList = pRhs;
|
||||
db->dbOptFlags |= SQLITE_QueryFlattener;
|
||||
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
|
||||
db->dbOptFlags = savedDbOptFlags;
|
||||
testcase( aiMap!=0 && aiMap[0]!=0 );
|
||||
pSelect->pEList = pOrigRhs;
|
||||
pLeft->x.pList = pOrigLhs;
|
||||
pX->pLeft = pLeft;
|
||||
pTerm->pExpr->iTable = pX->iTable;
|
||||
}
|
||||
sqlite3ExprListDelete(pParse->db, pLhs);
|
||||
sqlite3ExprListDelete(pParse->db, pRhs);
|
||||
sqlite3ExprDelete(db, pX);
|
||||
pX = pTerm->pExpr;
|
||||
}
|
||||
|
||||
if( eType==IN_INDEX_INDEX_DESC ){
|
||||
@@ -539,7 +590,14 @@ static int codeEqualityTerm(
|
||||
sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
|
||||
if( i==iEq ){
|
||||
pIn->iCur = iTab;
|
||||
pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
|
||||
pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next;
|
||||
if( iEq>0 && (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){
|
||||
pIn->iBase = iReg - i;
|
||||
pIn->nPrefix = i;
|
||||
pLoop->wsFlags |= WHERE_IN_EARLYOUT;
|
||||
}else{
|
||||
pIn->nPrefix = 0;
|
||||
}
|
||||
}else{
|
||||
pIn->eEndLoopOp = OP_Noop;
|
||||
}
|
||||
@@ -794,7 +852,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
|
||||
pWalker->eCode = 1;
|
||||
}else if( pExpr->op==TK_FUNCTION ){
|
||||
int d1;
|
||||
char d2[3];
|
||||
char d2[4];
|
||||
if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
|
||||
pWalker->eCode = 1;
|
||||
}
|
||||
@@ -826,11 +884,8 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
|
||||
struct CCurHint *pHint = pWalker->u.pCCurHint;
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
if( pExpr->iTable!=pHint->iTabCur ){
|
||||
Vdbe *v = pWalker->pParse->pVdbe;
|
||||
int reg = ++pWalker->pParse->nMem; /* Register for column value */
|
||||
sqlite3ExprCodeGetColumnOfTable(
|
||||
v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
|
||||
);
|
||||
sqlite3ExprCode(pWalker->pParse, pExpr, reg);
|
||||
pExpr->op = TK_REGISTER;
|
||||
pExpr->iTable = reg;
|
||||
}else if( pHint->pIdx!=0 ){
|
||||
@@ -1017,7 +1072,7 @@ static void codeDeferredSeek(
|
||||
*/
|
||||
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
||||
assert( nReg>0 );
|
||||
if( sqlite3ExprIsVector(p) ){
|
||||
if( p && sqlite3ExprIsVector(p) ){
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( (p->flags & EP_xIsSelect) ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
@@ -1070,9 +1125,9 @@ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
|
||||
}
|
||||
|
||||
/*
|
||||
** For an indexes on expression X, locate every instance of expression X in pExpr
|
||||
** and change that subexpression into a reference to the appropriate column of
|
||||
** the index.
|
||||
** For an indexes on expression X, locate every instance of expression X
|
||||
** in pExpr and change that subexpression into a reference to the appropriate
|
||||
** column of the index.
|
||||
*/
|
||||
static void whereIndexExprTrans(
|
||||
Index *pIdx, /* The Index */
|
||||
@@ -1163,6 +1218,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** initialize a memory cell that records if this table matches any
|
||||
** row of the left table of the join.
|
||||
*/
|
||||
assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
||||
|| pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
|
||||
);
|
||||
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
|
||||
pLevel->iLeftJoin = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
|
||||
@@ -1180,7 +1238,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
|
||||
pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
|
||||
VdbeCoverage(v);
|
||||
VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
|
||||
VdbeComment((v, "next row of %s", pTabItem->pTab->zName));
|
||||
pLevel->op = OP_Goto;
|
||||
}else
|
||||
|
||||
@@ -1194,7 +1252,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
int nConstraint = pLoop->nLTerm;
|
||||
int iIn; /* Counter for IN constraints */
|
||||
|
||||
sqlite3ExprCachePush(pParse);
|
||||
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
|
||||
addrNotFound = pLevel->addrBrk;
|
||||
for(j=0; j<nConstraint; j++){
|
||||
@@ -1267,7 +1324,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
**
|
||||
** sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
|
||||
*/
|
||||
sqlite3ExprCachePop(pParse);
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@@ -1291,9 +1347,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
addrNxt = pLevel->addrNxt;
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
|
||||
VdbeCoverage(v);
|
||||
sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
VdbeComment((v, "pk"));
|
||||
pLevel->op = OP_Noop;
|
||||
}else if( (pLoop->wsFlags & WHERE_IPK)!=0
|
||||
&& (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
|
||||
@@ -1343,7 +1396,15 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( sqlite3ExprIsVector(pX->pRight) ){
|
||||
r1 = rTemp = sqlite3GetTempReg(pParse);
|
||||
codeExprOrVector(pParse, pX->pRight, r1, 1);
|
||||
op = aMoveOp[(pX->op - TK_GT) | 0x0001];
|
||||
testcase( pX->op==TK_GT );
|
||||
testcase( pX->op==TK_GE );
|
||||
testcase( pX->op==TK_LT );
|
||||
testcase( pX->op==TK_LE );
|
||||
op = aMoveOp[((pX->op - TK_GT - 1) & 0x3) | 0x1];
|
||||
assert( pX->op!=TK_GT || op==OP_SeekGE );
|
||||
assert( pX->op!=TK_GE || op==OP_SeekGE );
|
||||
assert( pX->op!=TK_LT || op==OP_SeekLE );
|
||||
assert( pX->op!=TK_LE || op==OP_SeekLE );
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
|
||||
disableTerm(pLevel, pStart);
|
||||
@@ -1355,7 +1416,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
VdbeCoverageIf(v, pX->op==TK_LE);
|
||||
VdbeCoverageIf(v, pX->op==TK_LT);
|
||||
VdbeCoverageIf(v, pX->op==TK_GE);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
|
||||
sqlite3ReleaseTempReg(pParse, rTemp);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
|
||||
@@ -1390,7 +1450,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( testOp!=OP_Noop ){
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
|
||||
VdbeCoverageIf(v, testOp==OP_Le);
|
||||
VdbeCoverageIf(v, testOp==OP_Lt);
|
||||
@@ -1595,6 +1654,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** above has already left the cursor sitting on the correct row,
|
||||
** so no further seeking is needed */
|
||||
}else{
|
||||
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
|
||||
sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
|
||||
}
|
||||
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
|
||||
assert( op!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
|
||||
@@ -1613,7 +1675,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
nConstraint = nEq;
|
||||
if( pRangeEnd ){
|
||||
Expr *pRight = pRangeEnd->pExpr->pRight;
|
||||
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
||||
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
|
||||
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
|
||||
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
||||
@@ -1657,6 +1718,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
|
||||
}
|
||||
|
||||
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
|
||||
sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);
|
||||
}
|
||||
|
||||
/* Seek the table cursor, if required */
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
@@ -1667,7 +1732,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
)){
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
@@ -1687,9 +1751,16 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
/* If pIdx is an index on one or more expressions, then look through
|
||||
** all the expressions in pWInfo and try to transform matching expressions
|
||||
** into reference to index columns.
|
||||
**
|
||||
** Do not do this for the RHS of a LEFT JOIN. This is because the
|
||||
** expression may be evaluated after OP_NullRow has been executed on
|
||||
** the cursor. In this case it is important to do the full evaluation,
|
||||
** as the result of the expression may not be NULL, even if all table
|
||||
** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a
|
||||
*/
|
||||
whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
|
||||
|
||||
if( pLevel->iLeftJoin==0 ){
|
||||
whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
|
||||
}
|
||||
|
||||
/* Record the instruction used to terminate the loop. */
|
||||
if( pLoop->wsFlags & WHERE_ONEROW ){
|
||||
@@ -1845,7 +1916,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
|
||||
Expr *pExpr = pWC->a[iTerm].pExpr;
|
||||
if( &pWC->a[iTerm] == pTerm ) continue;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
|
||||
if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
|
||||
@@ -1864,13 +1934,17 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
|
||||
*/
|
||||
wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
|
||||
ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR"));
|
||||
for(ii=0; ii<pOrWc->nTerm; ii++){
|
||||
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
||||
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
|
||||
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
||||
Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
|
||||
int jmp1 = 0; /* Address of jump operation */
|
||||
if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
|
||||
assert( (pTabItem[0].fg.jointype & JT_LEFT)==0
|
||||
|| ExprHasProperty(pOrExpr, EP_FromJoin)
|
||||
);
|
||||
if( pAndExpr ){
|
||||
pAndExpr->pLeft = pOrExpr;
|
||||
pOrExpr = pAndExpr;
|
||||
}
|
||||
@@ -1882,7 +1956,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( pSubWInfo ){
|
||||
WhereLoop *pSubLoop;
|
||||
int addrExplain = sqlite3WhereExplainOneScan(
|
||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||
pParse, pOrTab, &pSubWInfo->a[0], 0
|
||||
);
|
||||
sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
|
||||
|
||||
@@ -1892,23 +1966,23 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** row will be skipped in subsequent sub-WHERE clauses.
|
||||
*/
|
||||
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||
int r;
|
||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||
if( HasRowid(pTab) ){
|
||||
r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, regRowid);
|
||||
jmp1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0,
|
||||
r,iSet);
|
||||
regRowid, iSet);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
int nPk = pPk->nKeyCol;
|
||||
int iPk;
|
||||
int r;
|
||||
|
||||
/* Read the PK into an array of temp registers. */
|
||||
r = sqlite3GetTempRange(pParse, nPk);
|
||||
for(iPk=0; iPk<nPk; iPk++){
|
||||
int iCol = pPk->aiColumn[iPk];
|
||||
sqlite3ExprCodeGetColumnToReg(pParse, pTab, iCol, iCur, r+iPk);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, r+iPk);
|
||||
}
|
||||
|
||||
/* Check if the temp table already contains this key. If so,
|
||||
@@ -1981,6 +2055,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
}
|
||||
}
|
||||
ExplainQueryPlanPop(pParse);
|
||||
pLevel->u.pCovidx = pCov;
|
||||
if( pCov ) pLevel->iIdxCur = iCovCur;
|
||||
if( pAndExpr ){
|
||||
@@ -2053,7 +2128,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
pE = pTerm->pExpr;
|
||||
assert( pE!=0 );
|
||||
if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
|
||||
if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2066,7 +2141,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
continue;
|
||||
}
|
||||
|
||||
if( pTerm->wtFlags & TERM_LIKECOND ){
|
||||
if( (pTerm->wtFlags & TERM_LIKECOND)!=0 ){
|
||||
/* If the TERM_LIKECOND flag is set, that means that the range search
|
||||
** is sufficient to guarantee that the LIKE operator is true, so we
|
||||
** can skip the call to the like(A,B) function. But this only works
|
||||
@@ -2076,8 +2151,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
continue;
|
||||
#else
|
||||
u32 x = pLevel->iLikeRepCntr;
|
||||
assert( x>0 );
|
||||
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1));
|
||||
if( x>0 ){
|
||||
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1));
|
||||
}
|
||||
VdbeCoverage(v);
|
||||
#endif
|
||||
}
|
||||
@@ -2117,6 +2193,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
WO_EQ|WO_IN|WO_IS, 0);
|
||||
if( pAlt==0 ) continue;
|
||||
if( pAlt->wtFlags & (TERM_CODED) ) continue;
|
||||
if( (pAlt->eOperator & WO_IN)
|
||||
&& (pAlt->pExpr->flags & EP_xIsSelect)
|
||||
&& (pAlt->pExpr->x.pSelect->pEList->nExpr>1)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
testcase( pAlt->eOperator & WO_EQ );
|
||||
testcase( pAlt->eOperator & WO_IS );
|
||||
testcase( pAlt->eOperator & WO_IN );
|
||||
@@ -2133,7 +2215,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
|
||||
VdbeComment((v, "record LEFT JOIN hit"));
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
testcase( pTerm->wtFlags & TERM_CODED );
|
||||
|
||||
+233
-109
@@ -194,18 +194,18 @@ static int isLikeOrGlob(
|
||||
int *pisComplete, /* True if the only wildcard is % in the last character */
|
||||
int *pnoCase /* True if uppercase is equivalent to lowercase */
|
||||
){
|
||||
const char *z = 0; /* String on RHS of LIKE operator */
|
||||
const u8 *z = 0; /* String on RHS of LIKE operator */
|
||||
Expr *pRight, *pLeft; /* Right and left size of LIKE operator */
|
||||
ExprList *pList; /* List of operands to the LIKE operator */
|
||||
int c; /* One character in z[] */
|
||||
u8 c; /* One character in z[] */
|
||||
int cnt; /* Number of non-wildcard prefix characters */
|
||||
char wc[3]; /* Wildcard characters */
|
||||
u8 wc[4]; /* Wildcard characters */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
sqlite3_value *pVal = 0;
|
||||
int op; /* Opcode of pRight */
|
||||
int rc; /* Result code to return */
|
||||
|
||||
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
|
||||
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, (char*)wc) ){
|
||||
return 0;
|
||||
}
|
||||
#ifdef SQLITE_EBCDIC
|
||||
@@ -221,41 +221,78 @@ static int isLikeOrGlob(
|
||||
int iCol = pRight->iColumn;
|
||||
pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
|
||||
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
||||
z = (char *)sqlite3_value_text(pVal);
|
||||
z = sqlite3_value_text(pVal);
|
||||
}
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
|
||||
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
|
||||
}else if( op==TK_STRING ){
|
||||
z = pRight->u.zToken;
|
||||
z = (u8*)pRight->u.zToken;
|
||||
}
|
||||
if( z ){
|
||||
|
||||
/* If the RHS begins with a digit or a minus sign, then the LHS must
|
||||
** be an ordinary column (not a virtual table column) with TEXT affinity.
|
||||
** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
|
||||
** even though "lhs LIKE rhs" is true. But if the RHS does not start
|
||||
** with a digit or '-', then "lhs LIKE rhs" will always be false if
|
||||
** the LHS is numeric and so the optimization still works.
|
||||
*/
|
||||
if( sqlite3Isdigit(z[0]) || z[0]=='-' ){
|
||||
if( pLeft->op!=TK_COLUMN
|
||||
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
||||
|| IsVirtual(pLeft->pTab) /* Value might be numeric */
|
||||
){
|
||||
sqlite3ValueFree(pVal);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Count the number of prefix characters prior to the first wildcard */
|
||||
cnt = 0;
|
||||
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
|
||||
cnt++;
|
||||
if( c==wc[3] && z[cnt]!=0 ) cnt++;
|
||||
}
|
||||
if( cnt!=0 && 255!=(u8)z[cnt-1] ){
|
||||
|
||||
/* The optimization is possible only if (1) the pattern does not begin
|
||||
** with a wildcard and if (2) the non-wildcard prefix does not end with
|
||||
** an (illegal 0xff) character, or (3) the pattern does not consist of
|
||||
** a single escape character. The second condition is necessary so
|
||||
** that we can increment the prefix key to find an upper bound for the
|
||||
** range search. The third is because the caller assumes that the pattern
|
||||
** consists of at least one character after all escapes have been
|
||||
** removed. */
|
||||
if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){
|
||||
Expr *pPrefix;
|
||||
|
||||
/* A "complete" match if the pattern ends with "*" or "%" */
|
||||
*pisComplete = c==wc[0] && z[cnt+1]==0;
|
||||
pPrefix = sqlite3Expr(db, TK_STRING, z);
|
||||
if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
|
||||
|
||||
/* Get the pattern prefix. Remove all escapes from the prefix. */
|
||||
pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
|
||||
if( pPrefix ){
|
||||
int iFrom, iTo;
|
||||
char *zNew = pPrefix->u.zToken;
|
||||
zNew[cnt] = 0;
|
||||
for(iFrom=iTo=0; iFrom<cnt; iFrom++){
|
||||
if( zNew[iFrom]==wc[3] ) iFrom++;
|
||||
zNew[iTo++] = zNew[iFrom];
|
||||
}
|
||||
zNew[iTo] = 0;
|
||||
|
||||
/* If the RHS begins with a digit or a minus sign, then the LHS must be
|
||||
** an ordinary column (not a virtual table column) with TEXT affinity.
|
||||
** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
|
||||
** even though "lhs LIKE rhs" is true. But if the RHS does not start
|
||||
** with a digit or '-', then "lhs LIKE rhs" will always be false if
|
||||
** the LHS is numeric and so the optimization still works.
|
||||
**
|
||||
** 2018-09-10 ticket c94369cae9b561b1f996d0054bfab11389f9d033
|
||||
** The RHS pattern must not be '/%' because the termination condition
|
||||
** will then become "x<'0'" and if the affinity is numeric, will then
|
||||
** be converted into "x<0", which is incorrect.
|
||||
*/
|
||||
if( sqlite3Isdigit(zNew[0])
|
||||
|| zNew[0]=='-'
|
||||
|| (zNew[0]+1=='0' && iTo==1)
|
||||
){
|
||||
if( pLeft->op!=TK_COLUMN
|
||||
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
||||
|| IsVirtual(pLeft->pTab) /* Value might be numeric */
|
||||
){
|
||||
sqlite3ExprDelete(db, pPrefix);
|
||||
sqlite3ValueFree(pVal);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
*ppPrefix = pPrefix;
|
||||
|
||||
/* If the RHS pattern is a bound parameter, make arrangements to
|
||||
** reprepare the statement when that parameter is rebound */
|
||||
if( op==TK_VARIABLE ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn);
|
||||
@@ -286,48 +323,123 @@ static int isLikeOrGlob(
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Check to see if the given expression is of the form
|
||||
** Check to see if the pExpr expression is a form that needs to be passed
|
||||
** to the xBestIndex method of virtual tables. Forms of interest include:
|
||||
**
|
||||
** column OP expr
|
||||
** Expression Virtual Table Operator
|
||||
** ----------------------- ---------------------------------
|
||||
** 1. column MATCH expr SQLITE_INDEX_CONSTRAINT_MATCH
|
||||
** 2. column GLOB expr SQLITE_INDEX_CONSTRAINT_GLOB
|
||||
** 3. column LIKE expr SQLITE_INDEX_CONSTRAINT_LIKE
|
||||
** 4. column REGEXP expr SQLITE_INDEX_CONSTRAINT_REGEXP
|
||||
** 5. column != expr SQLITE_INDEX_CONSTRAINT_NE
|
||||
** 6. expr != column SQLITE_INDEX_CONSTRAINT_NE
|
||||
** 7. column IS NOT expr SQLITE_INDEX_CONSTRAINT_ISNOT
|
||||
** 8. expr IS NOT column SQLITE_INDEX_CONSTRAINT_ISNOT
|
||||
** 9. column IS NOT NULL SQLITE_INDEX_CONSTRAINT_ISNOTNULL
|
||||
**
|
||||
** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a
|
||||
** column of a virtual table.
|
||||
** In every case, "column" must be a column of a virtual table. If there
|
||||
** is a match, set *ppLeft to the "column" expression, set *ppRight to the
|
||||
** "expr" expression (even though in forms (6) and (8) the column is on the
|
||||
** right and the expression is on the left). Also set *peOp2 to the
|
||||
** appropriate virtual table operator. The return value is 1 or 2 if there
|
||||
** is a match. The usual return is 1, but if the RHS is also a column
|
||||
** of virtual table in forms (5) or (7) then return 2.
|
||||
**
|
||||
** If it is then return TRUE. If not, return FALSE.
|
||||
** If the expression matches none of the patterns above, return 0.
|
||||
*/
|
||||
static int isMatchOfColumn(
|
||||
static int isAuxiliaryVtabOperator(
|
||||
sqlite3 *db, /* Parsing context */
|
||||
Expr *pExpr, /* Test this expression */
|
||||
unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
|
||||
unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */
|
||||
Expr **ppLeft, /* Column expression to left of MATCH/op2 */
|
||||
Expr **ppRight /* Expression to left of MATCH/op2 */
|
||||
){
|
||||
static const struct Op2 {
|
||||
const char *zOp;
|
||||
unsigned char eOp2;
|
||||
} aOp[] = {
|
||||
{ "match", SQLITE_INDEX_CONSTRAINT_MATCH },
|
||||
{ "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
|
||||
{ "like", SQLITE_INDEX_CONSTRAINT_LIKE },
|
||||
{ "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
|
||||
};
|
||||
ExprList *pList;
|
||||
Expr *pCol; /* Column reference */
|
||||
int i;
|
||||
if( pExpr->op==TK_FUNCTION ){
|
||||
static const struct Op2 {
|
||||
const char *zOp;
|
||||
unsigned char eOp2;
|
||||
} aOp[] = {
|
||||
{ "match", SQLITE_INDEX_CONSTRAINT_MATCH },
|
||||
{ "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
|
||||
{ "like", SQLITE_INDEX_CONSTRAINT_LIKE },
|
||||
{ "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
|
||||
};
|
||||
ExprList *pList;
|
||||
Expr *pCol; /* Column reference */
|
||||
int i;
|
||||
|
||||
if( pExpr->op!=TK_FUNCTION ){
|
||||
return 0;
|
||||
}
|
||||
pList = pExpr->x.pList;
|
||||
if( pList==0 || pList->nExpr!=2 ){
|
||||
return 0;
|
||||
}
|
||||
pCol = pList->a[1].pExpr;
|
||||
if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<ArraySize(aOp); i++){
|
||||
if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
|
||||
*peOp2 = aOp[i].eOp2;
|
||||
return 1;
|
||||
pList = pExpr->x.pList;
|
||||
if( pList==0 || pList->nExpr!=2 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Built-in operators MATCH, GLOB, LIKE, and REGEXP attach to a
|
||||
** virtual table on their second argument, which is the same as
|
||||
** the left-hand side operand in their in-fix form.
|
||||
**
|
||||
** vtab_column MATCH expression
|
||||
** MATCH(expression,vtab_column)
|
||||
*/
|
||||
pCol = pList->a[1].pExpr;
|
||||
if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
|
||||
for(i=0; i<ArraySize(aOp); i++){
|
||||
if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
|
||||
*peOp2 = aOp[i].eOp2;
|
||||
*ppRight = pList->a[0].pExpr;
|
||||
*ppLeft = pCol;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We can also match against the first column of overloaded
|
||||
** functions where xFindFunction returns a value of at least
|
||||
** SQLITE_INDEX_CONSTRAINT_FUNCTION.
|
||||
**
|
||||
** OVERLOADED(vtab_column,expression)
|
||||
**
|
||||
** Historically, xFindFunction expected to see lower-case function
|
||||
** names. But for this use case, xFindFunction is expected to deal
|
||||
** with function names in an arbitrary case.
|
||||
*/
|
||||
pCol = pList->a[0].pExpr;
|
||||
if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
|
||||
sqlite3_vtab *pVtab;
|
||||
sqlite3_module *pMod;
|
||||
void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**);
|
||||
void *pNotUsed;
|
||||
pVtab = sqlite3GetVTable(db, pCol->pTab)->pVtab;
|
||||
assert( pVtab!=0 );
|
||||
assert( pVtab->pModule!=0 );
|
||||
pMod = (sqlite3_module *)pVtab->pModule;
|
||||
if( pMod->xFindFunction!=0 ){
|
||||
i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed);
|
||||
if( i>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
|
||||
*peOp2 = i;
|
||||
*ppRight = pList->a[1].pExpr;
|
||||
*ppLeft = pCol;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){
|
||||
int res = 0;
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
Expr *pRight = pExpr->pRight;
|
||||
if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->pTab) ){
|
||||
res++;
|
||||
}
|
||||
if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->pTab) ){
|
||||
res++;
|
||||
SWAP(Expr*, pLeft, pRight);
|
||||
}
|
||||
*ppLeft = pLeft;
|
||||
*ppRight = pRight;
|
||||
if( pExpr->op==TK_NE ) *peOp2 = SQLITE_INDEX_CONSTRAINT_NE;
|
||||
if( pExpr->op==TK_ISNOT ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOT;
|
||||
if( pExpr->op==TK_NOTNULL ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOTNULL;
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -578,7 +690,7 @@ static void exprAnalyzeOrTerm(
|
||||
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
|
||||
assert( pAndTerm->pExpr );
|
||||
if( allowedOp(pAndTerm->pExpr->op)
|
||||
|| pAndTerm->eOperator==WO_MATCH
|
||||
|| pAndTerm->eOperator==WO_AUX
|
||||
){
|
||||
b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
|
||||
}
|
||||
@@ -610,7 +722,12 @@ static void exprAnalyzeOrTerm(
|
||||
** empty.
|
||||
*/
|
||||
pOrInfo->indexable = indexable;
|
||||
pTerm->eOperator = indexable==0 ? 0 : WO_OR;
|
||||
if( indexable ){
|
||||
pTerm->eOperator = WO_OR;
|
||||
pWC->hasOr = 1;
|
||||
}else{
|
||||
pTerm->eOperator = WO_OR;
|
||||
}
|
||||
|
||||
/* For a two-way OR, attempt to implementation case 2.
|
||||
*/
|
||||
@@ -751,12 +868,11 @@ static void exprAnalyzeOrTerm(
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
/* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
}else{
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
}
|
||||
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,7 +896,6 @@ static void exprAnalyzeOrTerm(
|
||||
static int termIsEquivalence(Parse *pParse, Expr *pExpr){
|
||||
char aff1, aff2;
|
||||
CollSeq *pColl;
|
||||
const char *zColl1, *zColl2;
|
||||
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
|
||||
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
|
||||
@@ -792,12 +907,8 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
|
||||
return 0;
|
||||
}
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
|
||||
if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
|
||||
pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
zColl1 = pColl ? pColl->zName : 0;
|
||||
pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
|
||||
zColl2 = pColl ? pColl->zName : 0;
|
||||
return sqlite3_stricmp(zColl1, zColl2)==0;
|
||||
if( sqlite3IsBinary(pColl) ) return 1;
|
||||
return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -819,6 +930,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
|
||||
mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
|
||||
if( pSrc->a[i].fg.isTabFunc ){
|
||||
mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
pS = pS->pPrior;
|
||||
@@ -926,7 +1040,7 @@ static void exprAnalyze(
|
||||
int op; /* Top-level operator. pExpr->op */
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */
|
||||
unsigned char eOp2 = 0; /* op2 value for LIKE/REGEXP/GLOB */
|
||||
int nLeft; /* Number of elements on left side vector */
|
||||
|
||||
if( db->mallocFailed ){
|
||||
@@ -952,7 +1066,7 @@ static void exprAnalyze(
|
||||
pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight);
|
||||
}
|
||||
pMaskSet->bVarSelect = 0;
|
||||
prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr);
|
||||
prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr);
|
||||
if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable);
|
||||
@@ -1134,7 +1248,7 @@ static void exprAnalyze(
|
||||
}
|
||||
*pC = c + 1;
|
||||
}
|
||||
zCollSeqName = noCase ? "NOCASE" : "BINARY";
|
||||
zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY;
|
||||
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
|
||||
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
|
||||
sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
|
||||
@@ -1160,41 +1274,46 @@ static void exprAnalyze(
|
||||
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Add a WO_MATCH auxiliary term to the constraint set if the
|
||||
** current expression is of the form: column MATCH expr.
|
||||
/* Add a WO_AUX auxiliary term to the constraint set if the
|
||||
** current expression is of the form "column OP expr" where OP
|
||||
** is an operator that gets passed into virtual tables but which is
|
||||
** not normally optimized for ordinary tables. In other words, OP
|
||||
** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL.
|
||||
** This information is used by the xBestIndex methods of
|
||||
** virtual tables. The native query optimizer does not attempt
|
||||
** to do anything with MATCH functions.
|
||||
*/
|
||||
if( pWC->op==TK_AND && isMatchOfColumn(pExpr, &eOp2) ){
|
||||
int idxNew;
|
||||
Expr *pRight, *pLeft;
|
||||
WhereTerm *pNewTerm;
|
||||
Bitmask prereqColumn, prereqExpr;
|
||||
if( pWC->op==TK_AND ){
|
||||
Expr *pRight = 0, *pLeft = 0;
|
||||
int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
|
||||
while( res-- > 0 ){
|
||||
int idxNew;
|
||||
WhereTerm *pNewTerm;
|
||||
Bitmask prereqColumn, prereqExpr;
|
||||
|
||||
pRight = pExpr->x.pList->a[0].pExpr;
|
||||
pLeft = pExpr->x.pList->a[1].pExpr;
|
||||
prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
|
||||
prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
|
||||
if( (prereqExpr & prereqColumn)==0 ){
|
||||
Expr *pNewExpr;
|
||||
pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
|
||||
0, sqlite3ExprDup(db, pRight, 0));
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
|
||||
ExprSetProperty(pNewExpr, EP_FromJoin);
|
||||
prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
|
||||
prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
|
||||
if( (prereqExpr & prereqColumn)==0 ){
|
||||
Expr *pNewExpr;
|
||||
pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
|
||||
0, sqlite3ExprDup(db, pRight, 0));
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
|
||||
ExprSetProperty(pNewExpr, EP_FromJoin);
|
||||
}
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
pNewTerm->prereqRight = prereqExpr;
|
||||
pNewTerm->leftCursor = pLeft->iTable;
|
||||
pNewTerm->u.leftColumn = pLeft->iColumn;
|
||||
pNewTerm->eOperator = WO_AUX;
|
||||
pNewTerm->eMatchOp = eOp2;
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_COPIED;
|
||||
pNewTerm->prereqAll = pTerm->prereqAll;
|
||||
}
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
pNewTerm->prereqRight = prereqExpr;
|
||||
pNewTerm->leftCursor = pLeft->iTable;
|
||||
pNewTerm->u.leftColumn = pLeft->iColumn;
|
||||
pNewTerm->eOperator = WO_MATCH;
|
||||
pNewTerm->eMatchOp = eOp2;
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_COPIED;
|
||||
pNewTerm->prereqAll = pTerm->prereqAll;
|
||||
SWAP(Expr*, pLeft, pRight);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
@@ -1226,7 +1345,7 @@ static void exprAnalyze(
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->eOperator = 0;
|
||||
}
|
||||
|
||||
@@ -1340,6 +1459,7 @@ void sqlite3WhereClauseInit(
|
||||
WhereInfo *pWInfo /* The WHERE processing context */
|
||||
){
|
||||
pWC->pWInfo = pWInfo;
|
||||
pWC->hasOr = 0;
|
||||
pWC->pOuter = 0;
|
||||
pWC->nTerm = 0;
|
||||
pWC->nSlot = ArraySize(pWC->aStatic);
|
||||
@@ -1376,17 +1496,18 @@ void sqlite3WhereClauseClear(WhereClause *pWC){
|
||||
** a bitmask indicating which tables are used in that expression
|
||||
** tree.
|
||||
*/
|
||||
Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
||||
Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
|
||||
Bitmask mask;
|
||||
if( p==0 ) return 0;
|
||||
if( p->op==TK_COLUMN ){
|
||||
if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
|
||||
return sqlite3WhereGetMask(pMaskSet, p->iTable);
|
||||
}else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
|
||||
assert( p->op!=TK_IF_NULL_ROW );
|
||||
return 0;
|
||||
}
|
||||
mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0;
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly) );
|
||||
if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
|
||||
if( p->pLeft ) mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pLeft);
|
||||
if( p->pRight ){
|
||||
mask |= sqlite3WhereExprUsage(pMaskSet, p->pRight);
|
||||
mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pRight);
|
||||
assert( p->x.pList==0 );
|
||||
}else if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1;
|
||||
@@ -1396,6 +1517,9 @@ Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
||||
return p ? sqlite3WhereExprUsageNN(pMaskSet,p) : 0;
|
||||
}
|
||||
Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprList *pList){
|
||||
int i;
|
||||
Bitmask mask = 0;
|
||||
|
||||
+2252
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user