mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-31 06:21:07 +00:00
Merge sqlite-release(3.15.2) into prerelease-integration
This commit is contained in:
+18
-22
@@ -229,7 +229,7 @@ static void renameTriggerFunc(
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
void sqlite3AlterFunctions(void){
|
||||
static SQLITE_WSD FuncDef aAlterTableFuncs[] = {
|
||||
static FuncDef aAlterTableFuncs[] = {
|
||||
FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
|
||||
@@ -238,13 +238,7 @@ void sqlite3AlterFunctions(void){
|
||||
FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs);
|
||||
|
||||
for(i=0; i<ArraySize(aAlterTableFuncs); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -419,7 +413,7 @@ void sqlite3AlterRenameTable(
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( !pTab ) goto exit_rename_table;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
db->flags |= SQLITE_PreferBuiltin;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
@@ -607,6 +601,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
Expr *pDflt; /* Default value for the new column */
|
||||
sqlite3 *db; /* The database connection; */
|
||||
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
|
||||
int r1; /* Temporary registers */
|
||||
|
||||
db = pParse->db;
|
||||
if( pParse->nErr || db->mallocFailed ) return;
|
||||
@@ -616,7 +611,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
|
||||
pCol = &pNew->aCol[pNew->nCol-1];
|
||||
pDflt = pCol->pDflt;
|
||||
@@ -634,7 +629,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
** literal NULL, then set pDflt to 0. This simplifies checking
|
||||
** for an SQL NULL default below.
|
||||
*/
|
||||
if( pDflt && pDflt->op==TK_NULL ){
|
||||
assert( pDflt==0 || pDflt->op==TK_SPAN );
|
||||
if( pDflt && pDflt->pLeft->op==TK_NULL ){
|
||||
pDflt = 0;
|
||||
}
|
||||
|
||||
@@ -700,16 +696,18 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
db->flags = savedDbFlags;
|
||||
}
|
||||
|
||||
/* If the default value of the new column is NULL, then the file
|
||||
** format to 2. If the default value of the new column is not NULL,
|
||||
** the file format be 3. Back when this feature was first added
|
||||
** in 2006, we went to the trouble to upgrade the file format to the
|
||||
** minimum support values. But 10-years on, we can assume that all
|
||||
** extent versions of SQLite support file-format 4, so we always and
|
||||
** unconditionally upgrade to 4.
|
||||
/* Make sure the schema version is at least 3. But do not upgrade
|
||||
** from less than 3 to 4, as that will corrupt any preexisting DESC
|
||||
** index.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT,
|
||||
SQLITE_MAX_FILE_FORMAT);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
|
||||
sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
|
||||
/* Reload the schema of the modified table. */
|
||||
reloadTableSchema(pParse, pTab, pTab->zName);
|
||||
@@ -791,9 +789,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
Column *pCol = &pNew->aCol[i];
|
||||
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
|
||||
pCol->zColl = 0;
|
||||
pCol->zType = 0;
|
||||
pCol->pDflt = 0;
|
||||
pCol->zDflt = 0;
|
||||
}
|
||||
pNew->pSchema = db->aDb[iDb].pSchema;
|
||||
pNew->addColOffset = pTab->addColOffset;
|
||||
|
||||
+31
-31
@@ -210,14 +210,14 @@ static void openStatTable(
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
|
||||
if( aTable[i].zCols ){
|
||||
/* The sqlite_statN table does not exist. Create it. Note that a
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse->regRoot. This is important
|
||||
** because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
|
||||
);
|
||||
aRoot[i] = pParse->regRoot;
|
||||
aCreateTbl[i] = OPFLAG_P2ISREG;
|
||||
@@ -232,7 +232,7 @@ static void openStatTable(
|
||||
if( zWhere ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q",
|
||||
pDb->zName, zTab, zWhereType, zWhere
|
||||
pDb->zDbSName, zTab, zWhereType, zWhere
|
||||
);
|
||||
}else{
|
||||
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
||||
@@ -481,8 +481,7 @@ static const FuncDef statInitFuncdef = {
|
||||
statInit, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_init", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
{0}
|
||||
};
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
@@ -781,8 +780,7 @@ static const FuncDef statPushFuncdef = {
|
||||
statPush, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_push", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
{0}
|
||||
};
|
||||
|
||||
#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
|
||||
@@ -927,8 +925,7 @@ static const FuncDef statGetFuncdef = {
|
||||
statGet, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_get", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
{0}
|
||||
};
|
||||
|
||||
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
|
||||
@@ -997,7 +994,7 @@ static void analyzeOneTable(
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1387,7 +1384,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
|
||||
/* Form 3: Analyze the fully qualified table name */
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
|
||||
if( iDb>=0 ){
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
z = sqlite3NameFromToken(db, pTableName);
|
||||
if( z ){
|
||||
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
|
||||
@@ -1673,7 +1670,7 @@ static int loadStatTbl(
|
||||
assert( db->lookaside.bDisable );
|
||||
zSql = sqlite3MPrintf(db, zSql1, zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
@@ -1713,7 +1710,7 @@ static int loadStatTbl(
|
||||
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
|
||||
if( pIdx->aSample==0 ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pSpace = (tRowcnt*)&pIdx->aSample[nSample];
|
||||
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
|
||||
@@ -1729,7 +1726,7 @@ static int loadStatTbl(
|
||||
|
||||
zSql = sqlite3MPrintf(db, zSql2, zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
@@ -1767,7 +1764,7 @@ static int loadStatTbl(
|
||||
pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
|
||||
if( pSample->p==0 ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n);
|
||||
pIdx->nSample++;
|
||||
@@ -1829,7 +1826,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
analysisInfo sInfo;
|
||||
HashElem *i;
|
||||
char *zSql;
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pBt!=0 );
|
||||
@@ -1838,30 +1835,33 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3DefaultRowEst(pIdx);
|
||||
pIdx->aiRowLogEst[0] = 0;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
sqlite3DeleteIndexSamples(db, pIdx);
|
||||
pIdx->aSample = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table exists */
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db->aDb[iDb].zName;
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set appropriate defaults on all indexes not in the sqlite_stat1 table */
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
if( pIdx->aiRowLogEst[0]==0 ) sqlite3DefaultRowEst(pIdx);
|
||||
}
|
||||
|
||||
/* Load the statistics from the sqlite_stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
|
||||
+13
-14
@@ -97,7 +97,7 @@ static void attachFunc(
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zName;
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
@@ -144,7 +144,7 @@ static void attachFunc(
|
||||
Pager *pPager;
|
||||
aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
|
||||
if( !aNew->pSchema ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
|
||||
zErrDyn = sqlite3MPrintf(db,
|
||||
"attached databases must use the same text encoding as main database");
|
||||
@@ -161,10 +161,10 @@ static void attachFunc(
|
||||
#endif
|
||||
sqlite3BtreeLeave(aNew->pBt);
|
||||
}
|
||||
aNew->safety_level = 3;
|
||||
aNew->zName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zName==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
aNew->zDbSName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zDbSName==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ static void attachFunc(
|
||||
case SQLITE_NULL:
|
||||
/* No key specified. Use the key from the main database */
|
||||
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
|
||||
if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
|
||||
if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
|
||||
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
}
|
||||
break;
|
||||
@@ -275,7 +275,7 @@ static void detachFunc(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ) continue;
|
||||
if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
|
||||
if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
|
||||
}
|
||||
|
||||
if( i>=db->nDb ){
|
||||
@@ -325,6 +325,7 @@ static void codeAttach(
|
||||
sqlite3* db = pParse->db;
|
||||
int regArgs;
|
||||
|
||||
if( pParse->nErr ) goto attach_end;
|
||||
memset(&sName, 0, sizeof(NameContext));
|
||||
sName.pParse = pParse;
|
||||
|
||||
@@ -392,8 +393,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
detachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"sqlite_detach", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
{0}
|
||||
};
|
||||
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
|
||||
}
|
||||
@@ -412,8 +412,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
attachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"sqlite_attach", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
{0}
|
||||
};
|
||||
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
|
||||
}
|
||||
@@ -435,7 +434,7 @@ void sqlite3FixInit(
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zDb = db->aDb[iDb].zDbSName;
|
||||
pFix->pSchema = db->aDb[iDb].pSchema;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
@@ -532,7 +531,7 @@ int sqlite3FixExpr(
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
|
||||
+4
-3
@@ -107,10 +107,11 @@ int sqlite3AuthReadCol(
|
||||
const char *zCol, /* Column name */
|
||||
int iDb /* Index of containing database. */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zName; /* Name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
,db->auth.zAuthUser
|
||||
|
||||
+30
-27
@@ -83,22 +83,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
int i = sqlite3FindDbName(pDb, zDb);
|
||||
|
||||
if( i==1 ){
|
||||
Parse *pParse;
|
||||
Parse sParse;
|
||||
int rc = 0;
|
||||
pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory");
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pParse->db = pDb;
|
||||
if( sqlite3OpenTempDatabase(pParse) ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree(pErrorDb, pParse->zErrMsg);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(pErrorDb, pParse);
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = pDb;
|
||||
if( sqlite3OpenTempDatabase(&sParse) ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree(pErrorDb, sParse.zErrMsg);
|
||||
sqlite3ParserReset(&sParse);
|
||||
if( rc ){
|
||||
return 0;
|
||||
}
|
||||
@@ -182,7 +176,7 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
** sqlite3_backup_finish(). */
|
||||
p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup));
|
||||
if( !p ){
|
||||
sqlite3Error(pDestDb, SQLITE_NOMEM);
|
||||
sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +190,6 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
p->isAttached = 0;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest
|
||||
|| setDestPgsz(p)==SQLITE_NOMEM
|
||||
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
|
||||
){
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
@@ -384,14 +377,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* If there is no open read-transaction on the source database, open
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
@@ -401,6 +386,24 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
/* If the destination database has not yet been locked (i.e. if this
|
||||
** is the first call to backup_step() for the current backup operation),
|
||||
** try to set its page size to the same as the source database. This
|
||||
** is especially important on ZipVFS systems, as in that case it is
|
||||
** not possible to create a database file that uses one page size by
|
||||
** writing to it with another. */
|
||||
if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* Do not allow backup if the destination database is in WAL mode
|
||||
** and the page sizes are different between source and destination */
|
||||
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
|
||||
@@ -581,7 +584,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_IOERR_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
p->rc = rc;
|
||||
}
|
||||
@@ -777,10 +780,10 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
** sqlite3_backup_step(), we can guarantee that the copy finishes
|
||||
** within a single call (unless an error occurs). The assert() statement
|
||||
** checks this assumption - (p->rc) should be set to either SQLITE_DONE
|
||||
** or an error code.
|
||||
*/
|
||||
** or an error code. */
|
||||
sqlite3_backup_step(&b, 0x7FFFFFFF);
|
||||
assert( b.rc!=SQLITE_OK );
|
||||
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
|
||||
|
||||
+2
-2
@@ -177,7 +177,7 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
|
||||
i = i%p->iDivisor;
|
||||
if( p->u.apSub[bin]==0 ){
|
||||
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
|
||||
if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
|
||||
if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
p = p->u.apSub[bin];
|
||||
}
|
||||
@@ -212,7 +212,7 @@ bitvec_set_rehash:
|
||||
int rc;
|
||||
u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
|
||||
if( aiValues==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
|
||||
memset(p->u.apSub, 0, sizeof(p->u.apSub));
|
||||
|
||||
+183
-171
@@ -350,7 +350,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
|
||||
if( !pLock ){
|
||||
pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
|
||||
if( !pLock ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pLock->iTable = iTable;
|
||||
pLock->pBtree = p;
|
||||
@@ -450,6 +450,15 @@ static void releasePage(MemPage *pPage); /* Forward reference */
|
||||
static int cursorHoldsMutex(BtCursor *p){
|
||||
return sqlite3_mutex_held(p->pBt->mutex);
|
||||
}
|
||||
|
||||
/* Verify that the cursor and the BtShared agree about what is the current
|
||||
** database connetion. This is important in shared-cache mode. If the database
|
||||
** connection pointers get out-of-sync, it is possible for routines like
|
||||
** btreeInitPage() to reference an stale connection pointer that references a
|
||||
** a connection that has already closed. This routine is used inside assert()
|
||||
** statements only and for the purpose of double-checking that the btree code
|
||||
** does keep the database connection pointers up-to-date.
|
||||
*/
|
||||
static int cursorOwnsBtShared(BtCursor *p){
|
||||
assert( cursorHoldsMutex(p) );
|
||||
return (p->pBtree->db==p->pBt->db);
|
||||
@@ -553,7 +562,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
|
||||
assert( pgno<=pBt->nPage );
|
||||
pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage);
|
||||
if( !pBt->pHasContent ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){
|
||||
@@ -609,21 +618,19 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
|
||||
** the key.
|
||||
*/
|
||||
static int saveCursorKey(BtCursor *pCur){
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
assert( CURSOR_VALID==pCur->eState );
|
||||
assert( 0==pCur->pKey );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
|
||||
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
|
||||
assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
|
||||
|
||||
/* If this is an intKey table, then the above call to BtreeKeySize()
|
||||
** stores the integer key in pCur->nKey. In this case this value is
|
||||
** all that is required. Otherwise, if pCur is not open on an intKey
|
||||
** table, then malloc space for and store the pCur->nKey bytes of key
|
||||
** data. */
|
||||
if( 0==pCur->curIntKey ){
|
||||
void *pKey = sqlite3Malloc( pCur->nKey );
|
||||
if( pCur->curIntKey ){
|
||||
/* Only the rowid is required for a table btree */
|
||||
pCur->nKey = sqlite3BtreeIntegerKey(pCur);
|
||||
}else{
|
||||
/* For an index btree, save the complete key content */
|
||||
void *pKey;
|
||||
pCur->nKey = sqlite3BtreePayloadSize(pCur);
|
||||
pKey = sqlite3Malloc( pCur->nKey );
|
||||
if( pKey ){
|
||||
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -632,7 +639,7 @@ static int saveCursorKey(BtCursor *pCur){
|
||||
sqlite3_free(pKey);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
assert( !pCur->curIntKey || !pCur->pKey );
|
||||
@@ -756,7 +763,7 @@ static int btreeMoveto(
|
||||
){
|
||||
int rc; /* Status code */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked index key */
|
||||
char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char aSpace[384]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char *pFree = 0;
|
||||
|
||||
if( pKey ){
|
||||
@@ -764,7 +771,7 @@ static int btreeMoveto(
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
|
||||
pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
|
||||
);
|
||||
if( pIdxKey==0 ) return SQLITE_NOMEM;
|
||||
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
|
||||
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
|
||||
if( pIdxKey->nField==0 ){
|
||||
sqlite3DbFree(pCur->pKeyInfo->db, pFree);
|
||||
@@ -1598,8 +1605,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
if( data[iPtr+1]==0 && data[iPtr]==0 ){
|
||||
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
|
||||
}else{
|
||||
while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT;
|
||||
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ){
|
||||
if( iFreeBlk==0 ) break;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
iPtr = iFreeBlk;
|
||||
}
|
||||
if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
|
||||
@@ -1676,11 +1686,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pBt = pPage->pBt;
|
||||
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
|
||||
/* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior
|
||||
** table b-tree page. */
|
||||
/* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
|
||||
** interior table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
|
||||
/* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf
|
||||
** table b-tree page. */
|
||||
/* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
|
||||
** leaf table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
|
||||
pPage->intKey = 1;
|
||||
if( pPage->leaf ){
|
||||
@@ -1694,11 +1704,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else if( flagByte==PTF_ZERODATA ){
|
||||
/* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior
|
||||
** index b-tree page. */
|
||||
/* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
|
||||
** interior index b-tree page. */
|
||||
assert( (PTF_ZERODATA)==2 );
|
||||
/* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf
|
||||
** index b-tree page. */
|
||||
/* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
|
||||
** leaf index b-tree page. */
|
||||
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
@@ -2176,7 +2186,7 @@ int sqlite3BtreeOpen(
|
||||
}
|
||||
p = sqlite3MallocZero(sizeof(Btree));
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
p->inTrans = TRANS_NONE;
|
||||
p->db = db;
|
||||
@@ -2200,7 +2210,7 @@ int sqlite3BtreeOpen(
|
||||
p->sharable = 1;
|
||||
if( !zFullPathname ){
|
||||
sqlite3_free(p);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( isMemdb ){
|
||||
memcpy(zFullPathname, zFilename, nFilename);
|
||||
@@ -2268,7 +2278,7 @@ int sqlite3BtreeOpen(
|
||||
|
||||
pBt = sqlite3MallocZero( sizeof(*pBt) );
|
||||
if( pBt==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto btree_open_out;
|
||||
}
|
||||
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
|
||||
@@ -2330,14 +2340,14 @@ int sqlite3BtreeOpen(
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
|
||||
/* Add the new BtShared object to the linked list sharable BtShareds.
|
||||
*/
|
||||
pBt->nRef = 1;
|
||||
if( p->sharable ){
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
|
||||
pBt->nRef = 1;
|
||||
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
|
||||
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
|
||||
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
|
||||
if( pBt->mutex==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto btree_open_out;
|
||||
}
|
||||
}
|
||||
@@ -2360,12 +2370,12 @@ int sqlite3BtreeOpen(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){
|
||||
while( pSib->pPrev ){ pSib = pSib->pPrev; }
|
||||
if( p->pBt<pSib->pBt ){
|
||||
if( (uptr)p->pBt<(uptr)pSib->pBt ){
|
||||
p->pNext = pSib;
|
||||
p->pPrev = 0;
|
||||
pSib->pPrev = p;
|
||||
}else{
|
||||
while( pSib->pNext && pSib->pNext->pBt<p->pBt ){
|
||||
while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){
|
||||
pSib = pSib->pNext;
|
||||
}
|
||||
p->pNext = pSib->pNext;
|
||||
@@ -2403,6 +2413,7 @@ btree_open_out:
|
||||
assert( sqlite3_mutex_held(mutexOpen) );
|
||||
sqlite3_mutex_leave(mutexOpen);
|
||||
}
|
||||
assert( rc!=SQLITE_OK || sqlite3BtreeConnectionCount(*ppBtree)>0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2619,21 +2630,6 @@ int sqlite3BtreeSetPagerFlags(
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return TRUE if the given btree is set to safety level 1. In other
|
||||
** words, return TRUE if no sync() occurs on the disk files.
|
||||
*/
|
||||
int sqlite3BtreeSyncDisabled(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
int rc;
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( pBt && pBt->pPager );
|
||||
rc = sqlite3PagerNosync(pBt->pPager);
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the default pages size and the number of reserved bytes per page.
|
||||
** Or, if the page size has already been fixed, return SQLITE_READONLY
|
||||
@@ -2879,9 +2875,25 @@ static int lockBtree(BtShared *pBt){
|
||||
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto page1_init_failed;
|
||||
}else if( isOpen==0 ){
|
||||
releasePage(pPage1);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS
|
||||
sqlite3 *db;
|
||||
Db *pDb;
|
||||
if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){
|
||||
while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; }
|
||||
if( pDb->bSyncSet==0
|
||||
&& pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1
|
||||
){
|
||||
pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1;
|
||||
sqlite3PagerSetFlags(pBt->pPager,
|
||||
pDb->safety_level | (db->flags & PAGER_FLAGS_MASK));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( isOpen==0 ){
|
||||
releasePage(pPage1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
rc = SQLITE_NOTADB;
|
||||
}
|
||||
@@ -4114,7 +4126,7 @@ static int btreeCursor(
|
||||
|
||||
if( wrFlag ){
|
||||
allocateTempSpace(pBt);
|
||||
if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM;
|
||||
if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( iTable==1 && btreePagecount(pBt)==0 ){
|
||||
assert( wrFlag==0 );
|
||||
@@ -4261,46 +4273,33 @@ int sqlite3BtreeCursorIsValid(BtCursor *pCur){
|
||||
#endif /* NDEBUG */
|
||||
|
||||
/*
|
||||
** Set *pSize to the size of the buffer needed to hold the value of
|
||||
** the key for the current entry. If the cursor is not pointing
|
||||
** to a valid entry, *pSize is set to 0.
|
||||
**
|
||||
** For a table with the INTKEY flag set, this routine returns the key
|
||||
** itself, not the number of bytes in the key.
|
||||
**
|
||||
** The caller must position the cursor prior to invoking this routine.
|
||||
**
|
||||
** This routine cannot fail. It always returns SQLITE_OK.
|
||||
** Return the value of the integer key or "rowid" for a table btree.
|
||||
** This routine is only valid for a cursor that is pointing into a
|
||||
** ordinary table btree. If the cursor points to an index btree or
|
||||
** is invalid, the result of this routine is undefined.
|
||||
*/
|
||||
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
||||
i64 sqlite3BtreeIntegerKey(BtCursor *pCur){
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->curIntKey );
|
||||
getCellInfo(pCur);
|
||||
*pSize = pCur->info.nKey;
|
||||
return SQLITE_OK;
|
||||
return pCur->info.nKey;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set *pSize to the number of bytes of data in the entry the
|
||||
** cursor currently points to.
|
||||
** Return the number of bytes of payload for the entry that pCur is
|
||||
** currently pointing to. For table btrees, this will be the amount
|
||||
** of data. For index btrees, this will be the size of the key.
|
||||
**
|
||||
** The caller must guarantee that the cursor is pointing to a non-NULL
|
||||
** valid entry. In other words, the calling procedure must guarantee
|
||||
** that the cursor has Cursor.eState==CURSOR_VALID.
|
||||
**
|
||||
** Failure is not possible. This function always returns SQLITE_OK.
|
||||
** It might just as well be a procedure (returning void) but we continue
|
||||
** to return an integer result code for historical reasons.
|
||||
*/
|
||||
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
u32 sqlite3BtreePayloadSize(BtCursor *pCur){
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->iPage>=0 );
|
||||
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
|
||||
assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 );
|
||||
getCellInfo(pCur);
|
||||
*pSize = pCur->info.nPayload;
|
||||
return SQLITE_OK;
|
||||
return pCur->info.nPayload;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4471,8 +4470,13 @@ static int accessPayload(
|
||||
#endif
|
||||
assert( offset+amt <= pCur->info.nPayload );
|
||||
|
||||
if( &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){
|
||||
/* Trying to read or write past the end of the data is an error */
|
||||
assert( aPayload > pPage->aData );
|
||||
if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){
|
||||
/* Trying to read or write past the end of the data is an error. The
|
||||
** conditional above is really:
|
||||
** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
|
||||
** but is recast into its current form to avoid integer overflow problems
|
||||
*/
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
@@ -4512,7 +4516,7 @@ static int accessPayload(
|
||||
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pCur->nOvflAlloc = nOvfl*2;
|
||||
pCur->aOverflow = aNew;
|
||||
@@ -4597,7 +4601,7 @@ static int accessPayload(
|
||||
&& (bEnd || a==ovflSize) /* (6) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (4) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
|
||||
&& pBt->pPage1->aData[19]==0x01 /* (5) */
|
||||
&& 0==sqlite3PagerUseWal(pBt->pPager) /* (5) */
|
||||
&& &pBuf[-4]>=pBufStart /* (7) */
|
||||
){
|
||||
u8 aSave[4];
|
||||
@@ -4737,10 +4741,7 @@ static const void *fetchPayload(
|
||||
** These routines is used to get quick access to key and data
|
||||
** in the common case where no overflow pages are used.
|
||||
*/
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){
|
||||
return fetchPayload(pCur, pAmt);
|
||||
}
|
||||
const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){
|
||||
const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){
|
||||
return fetchPayload(pCur, pAmt);
|
||||
}
|
||||
|
||||
@@ -5073,11 +5074,12 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
assert( pRes );
|
||||
assert( (pIdxKey==0)==(pCur->pKeyInfo==0) );
|
||||
assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) );
|
||||
|
||||
/* If the cursor is already positioned at the point we are trying
|
||||
** to move to, then just return without doing any work */
|
||||
if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
|
||||
&& pCur->curIntKey
|
||||
if( pIdxKey==0
|
||||
&& pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
|
||||
){
|
||||
if( pCur->info.nKey==intKey ){
|
||||
*pRes = 0;
|
||||
@@ -5217,7 +5219,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
pCellKey = sqlite3Malloc( nCell+18 );
|
||||
if( pCellKey==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
@@ -6066,9 +6068,7 @@ static int clearCell(
|
||||
static int fillInCell(
|
||||
MemPage *pPage, /* The page that contains the cell */
|
||||
unsigned char *pCell, /* Complete text of the cell */
|
||||
const void *pKey, i64 nKey, /* The key */
|
||||
const void *pData,int nData, /* The data */
|
||||
int nZero, /* Extra zero bytes to append to pData */
|
||||
const BtreePayload *pX, /* Payload with which to construct the cell */
|
||||
int *pnSize /* Write cell size here */
|
||||
){
|
||||
int nPayload;
|
||||
@@ -6092,26 +6092,21 @@ static int fillInCell(
|
||||
|
||||
/* Fill in the header. */
|
||||
nHeader = pPage->childPtrSize;
|
||||
nPayload = nData + nZero;
|
||||
if( pPage->intKeyLeaf ){
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
}else{
|
||||
assert( nData==0 );
|
||||
assert( nZero==0 );
|
||||
}
|
||||
nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
|
||||
|
||||
/* Fill in the payload size */
|
||||
if( pPage->intKey ){
|
||||
pSrc = pData;
|
||||
nSrc = nData;
|
||||
nData = 0;
|
||||
}else{
|
||||
assert( nKey<=0x7fffffff && pKey!=0 );
|
||||
nPayload = (int)nKey;
|
||||
pSrc = pKey;
|
||||
nSrc = (int)nKey;
|
||||
nPayload = pX->nData + pX->nZero;
|
||||
pSrc = pX->pData;
|
||||
nSrc = pX->nData;
|
||||
assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey);
|
||||
}else{
|
||||
assert( pX->nKey<=0x7fffffff && pX->pKey!=0 );
|
||||
nSrc = nPayload = (int)pX->nKey;
|
||||
pSrc = pX->pKey;
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
}
|
||||
|
||||
/* Fill in the payload */
|
||||
if( nPayload<=pPage->maxLocal ){
|
||||
n = nHeader + nPayload;
|
||||
testcase( n==3 );
|
||||
@@ -6149,7 +6144,7 @@ static int fillInCell(
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, pCell, &info);
|
||||
assert( nHeader==(int)(info.pPayload - pCell) );
|
||||
assert( info.nKey==nKey );
|
||||
assert( info.nKey==pX->nKey );
|
||||
assert( *pnSize == info.nSize );
|
||||
assert( spaceLeft == info.nLocal );
|
||||
}
|
||||
@@ -6234,10 +6229,6 @@ static int fillInCell(
|
||||
pSrc += n;
|
||||
nSrc -= n;
|
||||
spaceLeft -= n;
|
||||
if( nSrc==0 ){
|
||||
nSrc = nData;
|
||||
pSrc = pData;
|
||||
}
|
||||
}
|
||||
releasePage(pToRelease);
|
||||
return SQLITE_OK;
|
||||
@@ -6304,6 +6295,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
** in pTemp or the original pCell) and also record its index.
|
||||
** Allocating a new entry in pPage->aCell[] implies that
|
||||
** pPage->nOverflow is incremented.
|
||||
**
|
||||
** *pRC must be SQLITE_OK when this routine is called.
|
||||
*/
|
||||
static void insertCell(
|
||||
MemPage *pPage, /* Page into which we are copying */
|
||||
@@ -6319,8 +6312,7 @@ static void insertCell(
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
|
||||
|
||||
if( *pRC ) return;
|
||||
|
||||
assert( *pRC==SQLITE_OK );
|
||||
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
|
||||
assert( MX_CELL(pPage->pBt)<=10921 );
|
||||
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
|
||||
@@ -6394,7 +6386,7 @@ static void insertCell(
|
||||
|
||||
/*
|
||||
** A CellArray object contains a cache of pointers and sizes for a
|
||||
** consecutive sequence of cells that might be held multiple pages.
|
||||
** consecutive sequence of cells that might be held on multiple pages.
|
||||
*/
|
||||
typedef struct CellArray CellArray;
|
||||
struct CellArray {
|
||||
@@ -6539,8 +6531,8 @@ static int pageInsertArray(
|
||||
u8 *pSlot;
|
||||
sz = cachedCellSize(pCArray, i);
|
||||
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
|
||||
if( (pData - pBegin)<sz ) return 1;
|
||||
pData -= sz;
|
||||
if( pData<pBegin ) return 1;
|
||||
pSlot = pData;
|
||||
}
|
||||
/* pSlot and pCArray->apCell[i] will never overlap on a well-formed
|
||||
@@ -6702,7 +6694,7 @@ static int editPage(
|
||||
for(i=0; i<nNew && !CORRUPT_DB; i++){
|
||||
u8 *pCell = pCArray->apCell[i+iNew];
|
||||
int iOff = get2byteAligned(&pPg->aCellIdx[i*2]);
|
||||
if( pCell>=aData && pCell<&aData[pPg->pBt->usableSize] ){
|
||||
if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){
|
||||
pCell = &pTmp[pCell - aData];
|
||||
}
|
||||
assert( 0==memcmp(pCell, &aData[iOff],
|
||||
@@ -6826,8 +6818,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
while( ((*(pOut++) = *(pCell++))&0x80) && pCell<pStop );
|
||||
|
||||
/* Insert the new divider cell into pParent. */
|
||||
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno, &rc);
|
||||
}
|
||||
|
||||
/* Set the right-child pointer of pParent to point to the new page. */
|
||||
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew);
|
||||
@@ -7036,7 +7030,7 @@ static int balance_nonroot(
|
||||
assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
|
||||
|
||||
if( !aOvflSpace ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
/* Find the sibling pages to balance. Also locate the cells in pParent
|
||||
@@ -7136,7 +7130,7 @@ static int balance_nonroot(
|
||||
assert( szScratch<=6*(int)pBt->pageSize );
|
||||
b.apCell = sqlite3ScratchMalloc( szScratch );
|
||||
if( b.apCell==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
b.szCell = (u16*)&b.apCell[nMaxCells];
|
||||
@@ -7347,7 +7341,7 @@ static int balance_nonroot(
|
||||
assert( r<nMaxCells );
|
||||
(void)cachedCellSize(&b, r);
|
||||
if( szRight!=0
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+2)) ){
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
|
||||
break;
|
||||
}
|
||||
szRight += b.szCell[d] + 2;
|
||||
@@ -7571,9 +7565,9 @@ static int balance_nonroot(
|
||||
** any cell). But it is important to pass the correct size to
|
||||
** insertCell(), so reparse the cell now.
|
||||
**
|
||||
** Note that this can never happen in an SQLite data file, as all
|
||||
** cells are at least 4 bytes. It only happens in b-trees used
|
||||
** to evaluate "IN (SELECT ...)" and similar clauses.
|
||||
** This can only happen for b-trees used to evaluate "IN (SELECT ...)"
|
||||
** and WITHOUT ROWID tables with exactly one column which is the
|
||||
** primary key.
|
||||
*/
|
||||
if( b.szCell[j]==4 ){
|
||||
assert(leafCorrection==4);
|
||||
@@ -7919,13 +7913,19 @@ static int balance(BtCursor *pCur){
|
||||
|
||||
|
||||
/*
|
||||
** Insert a new record into the BTree. The key is given by (pKey,nKey)
|
||||
** and the data is given by (pData,nData). The cursor is used only to
|
||||
** define what table the record should be inserted into. The cursor
|
||||
** is left pointing at a random location.
|
||||
** Insert a new record into the BTree. The content of the new record
|
||||
** is described by the pX object. The pCur cursor is used only to
|
||||
** define what table the record should be inserted into, and is left
|
||||
** pointing at a random location.
|
||||
**
|
||||
** For an INTKEY table, only the nKey value of the key is used. pKey is
|
||||
** ignored. For a ZERODATA table, the pData and nData are both ignored.
|
||||
** For a table btree (used for rowid tables), only the pX.nKey value of
|
||||
** the key is used. The pX.pKey value must be NULL. The pX.nKey is the
|
||||
** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields
|
||||
** hold the content of the row.
|
||||
**
|
||||
** For an index btree (used for indexes and WITHOUT ROWID tables), the
|
||||
** key is an arbitrary byte sequence stored in pX.pKey,nKey. The
|
||||
** pX.pData,nData,nZero fields must be zero.
|
||||
**
|
||||
** If the seekResult parameter is non-zero, then a successful call to
|
||||
** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
|
||||
@@ -7942,9 +7942,7 @@ static int balance(BtCursor *pCur){
|
||||
*/
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const void *pKey, i64 nKey, /* The key of the new record */
|
||||
const void *pData, int nData, /* The data of the new record */
|
||||
int nZero, /* Number of extra 0 bytes to append to data */
|
||||
const BtreePayload *pX, /* Content of the row to be inserted */
|
||||
int appendBias, /* True if this is likely an append */
|
||||
int seekResult /* Result of prior MovetoUnpacked() call */
|
||||
){
|
||||
@@ -7974,7 +7972,7 @@ int sqlite3BtreeInsert(
|
||||
** keys with no associated data. If the cursor was opened expecting an
|
||||
** intkey table, the caller should be inserting integer keys with a
|
||||
** blob of associated data. */
|
||||
assert( (pKey==0)==(pCur->pKeyInfo==0) );
|
||||
assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
|
||||
|
||||
/* Save the positions of any other cursors open on this table.
|
||||
**
|
||||
@@ -7993,38 +7991,38 @@ int sqlite3BtreeInsert(
|
||||
}
|
||||
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
assert( pKey==0 );
|
||||
assert( pX->pKey==0 );
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced */
|
||||
invalidateIncrblobCursors(p, nKey, 0);
|
||||
invalidateIncrblobCursors(p, pX->nKey, 0);
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0
|
||||
&& pCur->info.nKey==nKey-1 ){
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
loc = -1;
|
||||
}else if( loc==0 ){
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, nKey, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 ){
|
||||
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
||||
|
||||
pPage = pCur->apPage[pCur->iPage];
|
||||
assert( pPage->intKey || nKey>=0 );
|
||||
assert( pPage->intKey || pX->nKey>=0 );
|
||||
assert( pPage->leaf || !pPage->intKey );
|
||||
|
||||
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
|
||||
pCur->pgnoRoot, nKey, nData, pPage->pgno,
|
||||
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
|
||||
loc==0 ? "overwrite" : "new entry"));
|
||||
assert( pPage->isInit );
|
||||
newCell = pBt->pTmpSpace;
|
||||
assert( newCell!=0 );
|
||||
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
|
||||
rc = fillInCell(pPage, newCell, pX, &szNew);
|
||||
if( rc ) goto end_insert;
|
||||
assert( szNew==pPage->xCellSize(pPage, newCell) );
|
||||
assert( szNew <= MX_CELL_SIZE(pBt) );
|
||||
@@ -8050,6 +8048,7 @@ int sqlite3BtreeInsert(
|
||||
assert( pPage->leaf );
|
||||
}
|
||||
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
|
||||
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
|
||||
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
|
||||
|
||||
/* If no error has occurred and pPage has an overflow cell, call balance()
|
||||
@@ -8073,7 +8072,8 @@ int sqlite3BtreeInsert(
|
||||
** row without seeking the cursor. This can be a big performance boost.
|
||||
*/
|
||||
pCur->info.nSize = 0;
|
||||
if( rc==SQLITE_OK && pPage->nOverflow ){
|
||||
if( pPage->nOverflow ){
|
||||
assert( rc==SQLITE_OK );
|
||||
pCur->curFlags &= ~(BTCF_ValidNKey);
|
||||
rc = balance(pCur);
|
||||
|
||||
@@ -8134,6 +8134,28 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
pPage = pCur->apPage[iCellDepth];
|
||||
pCell = findCell(pPage, iCellIdx);
|
||||
|
||||
/* If the bPreserve flag is set to true, then the cursor position must
|
||||
** be preserved following this delete operation. If the current delete
|
||||
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||
** returning.
|
||||
**
|
||||
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||
if( bPreserve ){
|
||||
if( !pPage->leaf
|
||||
|| (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
|
||||
){
|
||||
/* A b-tree rebalance will be required after deleting this entry.
|
||||
** Save the cursor key. */
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc ) return rc;
|
||||
}else{
|
||||
bSkipnext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the page containing the entry to delete is not a leaf page, move
|
||||
** the cursor to the largest entry in the tree that is smaller than
|
||||
** the entry being deleted. This cell will replace the cell being deleted
|
||||
@@ -8160,28 +8182,6 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||
}
|
||||
|
||||
/* If the bPreserve flag is set to true, then the cursor position must
|
||||
** be preserved following this delete operation. If the current delete
|
||||
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||
** returning.
|
||||
**
|
||||
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||
if( bPreserve ){
|
||||
if( !pPage->leaf
|
||||
|| (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
|
||||
){
|
||||
/* A b-tree rebalance will be required after deleting this entry.
|
||||
** Save the cursor key. */
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc ) return rc;
|
||||
}else{
|
||||
bSkipnext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the page containing the entry to be deleted writable. Then free any
|
||||
** overflow pages associated with the entry and finally remove the cell
|
||||
** itself from within the page. */
|
||||
@@ -8209,7 +8209,9 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
pTmp = pBt->pTmpSpace;
|
||||
assert( pTmp!=0 );
|
||||
rc = sqlite3PagerWrite(pLeaf->pDbPage);
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
}
|
||||
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -9698,4 +9700,14 @@ int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
|
||||
int sqlite3BtreeSharable(Btree *p){
|
||||
return p->sharable;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of connections to the BtShared object accessed by
|
||||
** the Btree handle passed as the only argument. For private caches
|
||||
** this is always 1. For shared caches it may be 1 or greater.
|
||||
*/
|
||||
int sqlite3BtreeConnectionCount(Btree *p){
|
||||
testcase( p->sharable );
|
||||
return p->pBt->nRef;
|
||||
}
|
||||
#endif
|
||||
|
||||
+41
-11
@@ -13,8 +13,8 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
#ifndef SQLITE_BTREE_H
|
||||
#define SQLITE_BTREE_H
|
||||
|
||||
/* TODO: This definition is just included so other modules compile. It
|
||||
** needs to be revisited.
|
||||
@@ -39,6 +39,7 @@
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
typedef struct BtShared BtShared;
|
||||
typedef struct BtreePayload BtreePayload;
|
||||
|
||||
|
||||
int sqlite3BtreeOpen(
|
||||
@@ -68,7 +69,6 @@ int sqlite3BtreeSetSpillSize(Btree*,int);
|
||||
int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
|
||||
#endif
|
||||
int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
|
||||
int sqlite3BtreeSyncDisabled(Btree*);
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
|
||||
int sqlite3BtreeGetPageSize(Btree*);
|
||||
int sqlite3BtreeMaxPageCount(Btree*,int);
|
||||
@@ -90,7 +90,9 @@ int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *pBtree);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
|
||||
#endif
|
||||
int sqlite3BtreeSavepoint(Btree *, int, int);
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
@@ -251,26 +253,52 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
|
||||
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
|
||||
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
int nZero, int bias, int seekResult);
|
||||
/* An instance of the BtreePayload object describes the content of a single
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** This object is used to pass information into sqlite3BtreeInsert(). The
|
||||
** same information used to be passed as five separate parameters. But placing
|
||||
** the information into this object helps to keep the interface more
|
||||
** organized and understandable, and it also helps the resulting code to
|
||||
** run a little faster by using fewer registers for parameter passing.
|
||||
*/
|
||||
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 */
|
||||
int nData; /* Size of pData. 0 if none. */
|
||||
int nZero; /* Extra zero data appended after pData,nData */
|
||||
};
|
||||
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int bias, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
|
||||
i64 sqlite3BtreeIntegerKey(BtCursor*);
|
||||
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt);
|
||||
const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
||||
u32 sqlite3BtreePayloadSize(BtCursor*);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeIncrblobCursor(BtCursor *);
|
||||
#endif
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
|
||||
int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
|
||||
@@ -304,11 +332,13 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
void sqlite3BtreeEnterAll(sqlite3*);
|
||||
int sqlite3BtreeSharable(Btree*);
|
||||
void sqlite3BtreeEnterCursor(BtCursor*);
|
||||
int sqlite3BtreeConnectionCount(Btree*);
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeEnterAll(X)
|
||||
# define sqlite3BtreeSharable(X) 0
|
||||
# define sqlite3BtreeEnterCursor(X)
|
||||
# define sqlite3BtreeConnectionCount(X) 1
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
@@ -333,4 +363,4 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _BTREE_H_ */
|
||||
#endif /* SQLITE_BTREE_H */
|
||||
|
||||
+218
-197
@@ -146,15 +146,14 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
assert( !pParse->isMultiWrite
|
||||
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
|
||||
if( v ){
|
||||
while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){}
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
if( pParse->nTableLock>0 && db->init.busy==0 ){
|
||||
sqlite3UserAuthInit(db);
|
||||
if( db->auth.authLevel<UAUTH_User ){
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
sqlite3ErrorMsg(pParse, "user not authenticated");
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -173,14 +172,16 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
|
||||
sqlite3VdbeJumpHere(v, 0);
|
||||
for(iDb=0; iDb<db->nDb; iDb++){
|
||||
Schema *pSchema;
|
||||
if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
pSchema = db->aDb[iDb].pSchema;
|
||||
sqlite3VdbeAddOp4Int(v,
|
||||
OP_Transaction, /* Opcode */
|
||||
iDb, /* P1 */
|
||||
DbMaskTest(pParse->writeMask,iDb), /* P2 */
|
||||
pParse->cookieValue[iDb], /* P3 */
|
||||
db->aDb[iDb].pSchema->iGeneration /* P4 */
|
||||
pSchema->schema_cookie, /* P3 */
|
||||
pSchema->iGeneration /* P4 */
|
||||
);
|
||||
if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
|
||||
VdbeComment((v,
|
||||
@@ -231,16 +232,6 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
}else{
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* We are done with this Parse object. There is no need to de-initialize it */
|
||||
#if 0
|
||||
pParse->colNamesSet = 0;
|
||||
pParse->nTab = 0;
|
||||
pParse->nMem = 0;
|
||||
pParse->nSet = 0;
|
||||
pParse->nVar = 0;
|
||||
DbMaskZero(pParse->cookieMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -260,8 +251,7 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
|
||||
char *zSql;
|
||||
char *zErrMsg = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar))
|
||||
char saveBuf[SAVE_SZ];
|
||||
char saveBuf[PARSE_TAIL_SZ];
|
||||
|
||||
if( pParse->nErr ) return;
|
||||
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
|
||||
@@ -272,12 +262,12 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
|
||||
return; /* A malloc must have failed */
|
||||
}
|
||||
pParse->nested++;
|
||||
memcpy(saveBuf, &pParse->nVar, SAVE_SZ);
|
||||
memset(&pParse->nVar, 0, SAVE_SZ);
|
||||
memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
|
||||
memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
|
||||
sqlite3RunParser(pParse, zSql, &zErrMsg);
|
||||
sqlite3DbFree(db, zErrMsg);
|
||||
sqlite3DbFree(db, zSql);
|
||||
memcpy(&pParse->nVar, saveBuf, SAVE_SZ);
|
||||
memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
|
||||
pParse->nested--;
|
||||
}
|
||||
|
||||
@@ -318,10 +308,11 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
#endif
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -338,7 +329,7 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
*/
|
||||
Table *sqlite3LocateTable(
|
||||
Parse *pParse, /* context in which to report errors */
|
||||
int isView, /* True if looking for a VIEW rather than a TABLE */
|
||||
u32 flags, /* LOCATE_VIEW or LOCATE_NOERR */
|
||||
const char *zName, /* Name of the table we are looking for */
|
||||
const char *zDbase /* Name of the database. Might be NULL */
|
||||
){
|
||||
@@ -352,7 +343,7 @@ Table *sqlite3LocateTable(
|
||||
|
||||
p = sqlite3FindTable(pParse->db, zName, zDbase);
|
||||
if( p==0 ){
|
||||
const char *zMsg = isView ? "no such view" : "no such table";
|
||||
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
|
||||
/* If zName is the not the name of a table in the schema created using
|
||||
@@ -364,12 +355,14 @@ Table *sqlite3LocateTable(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( zDbase ){
|
||||
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
|
||||
if( (flags & LOCATE_NOERR)==0 ){
|
||||
if( zDbase ){
|
||||
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
|
||||
}
|
||||
pParse->checkSchema = 1;
|
||||
}
|
||||
pParse->checkSchema = 1;
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -386,18 +379,18 @@ Table *sqlite3LocateTable(
|
||||
*/
|
||||
Table *sqlite3LocateTableItem(
|
||||
Parse *pParse,
|
||||
int isView,
|
||||
u32 flags,
|
||||
struct SrcList_item *p
|
||||
){
|
||||
const char *zDb;
|
||||
assert( p->pSchema==0 || p->zDatabase==0 );
|
||||
if( p->pSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
|
||||
zDb = pParse->db->aDb[iDb].zName;
|
||||
zDb = pParse->db->aDb[iDb].zDbSName;
|
||||
}else{
|
||||
zDb = p->zDatabase;
|
||||
}
|
||||
return sqlite3LocateTable(pParse, isView, p->zName, zDb);
|
||||
return sqlite3LocateTable(pParse, flags, p->zName, zDb);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -421,7 +414,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
Schema *pSchema = db->aDb[j].pSchema;
|
||||
assert( pSchema );
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&pSchema->idxHash, zName);
|
||||
if( p ) break;
|
||||
@@ -490,8 +483,8 @@ void sqlite3CollapseDatabaseArray(sqlite3 *db){
|
||||
for(i=j=2; i<db->nDb; i++){
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
sqlite3DbFree(db, pDb->zName);
|
||||
pDb->zName = 0;
|
||||
sqlite3DbFree(db, pDb->zDbSName);
|
||||
pDb->zDbSName = 0;
|
||||
continue;
|
||||
}
|
||||
if( j<i ){
|
||||
@@ -571,8 +564,6 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
|
||||
for(i=0; i<pTable->nCol; i++, pCol++){
|
||||
sqlite3DbFree(db, pCol->zName);
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
sqlite3DbFree(db, pCol->zDflt);
|
||||
sqlite3DbFree(db, pCol->zType);
|
||||
sqlite3DbFree(db, pCol->zColl);
|
||||
}
|
||||
sqlite3DbFree(db, pTable->aCol);
|
||||
@@ -594,16 +585,10 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
|
||||
** db parameter can be used with db->pnBytesFreed to measure the memory
|
||||
** used by the Table object.
|
||||
*/
|
||||
void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
|
||||
Index *pIndex, *pNext;
|
||||
TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */
|
||||
|
||||
assert( !pTable || pTable->nRef>0 );
|
||||
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
if( !pTable ) return;
|
||||
if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
|
||||
|
||||
/* Record the number of outstanding lookaside allocations in schema Tables
|
||||
** prior to doing any free() operations. Since schema Tables do not use
|
||||
** lookaside, this number should not change. */
|
||||
@@ -613,8 +598,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Delete all indices associated with this table. */
|
||||
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pNext;
|
||||
assert( pIndex->pSchema==pTable->pSchema );
|
||||
if( !db || db->pnBytesFreed==0 ){
|
||||
assert( pIndex->pSchema==pTable->pSchema
|
||||
|| (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) );
|
||||
if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){
|
||||
char *zName = pIndex->zName;
|
||||
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
|
||||
&pIndex->pSchema->idxHash, zName, 0
|
||||
@@ -643,6 +629,13 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Verify that no lookaside memory was used by schema tables */
|
||||
assert( nLookaside==0 || nLookaside==db->lookaside.nOut );
|
||||
}
|
||||
void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
if( !pTable ) return;
|
||||
if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
|
||||
deleteTable(db, pTable);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Unlink the given table from the hash tables and the delete the
|
||||
@@ -710,12 +703,8 @@ int sqlite3FindDbName(sqlite3 *db, const char *zName){
|
||||
int i = -1; /* Database number */
|
||||
if( zName ){
|
||||
Db *pDb;
|
||||
int n = sqlite3Strlen30(zName);
|
||||
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
|
||||
if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) &&
|
||||
0==sqlite3StrICmp(pDb->zName, zName) ){
|
||||
break;
|
||||
}
|
||||
if( 0==sqlite3StrICmp(pDb->zDbSName, zName) ) break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
@@ -774,7 +763,7 @@ int sqlite3TwoPartName(
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
assert( db->init.iDb==0 || db->init.busy );
|
||||
assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
|
||||
iDb = db->init.iDb;
|
||||
*pUnqual = pName1;
|
||||
}
|
||||
@@ -885,7 +874,7 @@ void sqlite3StartTable(
|
||||
SQLITE_CREATE_VIEW,
|
||||
SQLITE_CREATE_TEMP_VIEW
|
||||
};
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -904,7 +893,7 @@ void sqlite3StartTable(
|
||||
** collisions.
|
||||
*/
|
||||
if( !IN_DECLARE_VTAB ){
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -927,7 +916,7 @@ void sqlite3StartTable(
|
||||
pTable = sqlite3DbMallocZero(db, sizeof(Table));
|
||||
if( pTable==0 ){
|
||||
assert( db->mallocFailed );
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
pParse->rc = SQLITE_NOMEM_BKPT;
|
||||
pParse->nErr++;
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -1043,10 +1032,11 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
|
||||
** first to get things going. Then this routine is called for each
|
||||
** column.
|
||||
*/
|
||||
void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
|
||||
Table *p;
|
||||
int i;
|
||||
char *z;
|
||||
char *zType;
|
||||
Column *pCol;
|
||||
sqlite3 *db = pParse->db;
|
||||
if( (p = pParse->pNewTable)==0 ) return;
|
||||
@@ -1056,8 +1046,11 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
z = sqlite3NameFromToken(db, pName);
|
||||
z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
|
||||
if( z==0 ) return;
|
||||
memcpy(z, pName->z, pName->n);
|
||||
z[pName->n] = 0;
|
||||
sqlite3Dequote(z);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
|
||||
@@ -1079,13 +1072,21 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
pCol->zName = z;
|
||||
sqlite3ColumnPropertiesFromName(p, pCol);
|
||||
|
||||
/* If there is no type specified, columns have the default affinity
|
||||
** 'BLOB'. If there is a type specified, then sqlite3AddColumnType() will
|
||||
** be called next to set pCol->affinity correctly.
|
||||
*/
|
||||
pCol->affinity = SQLITE_AFF_BLOB;
|
||||
pCol->szEst = 1;
|
||||
if( pType->n==0 ){
|
||||
/* If there is no type specified, columns have the default affinity
|
||||
** 'BLOB'. */
|
||||
pCol->affinity = SQLITE_AFF_BLOB;
|
||||
pCol->szEst = 1;
|
||||
}else{
|
||||
zType = z + sqlite3Strlen30(z) + 1;
|
||||
memcpy(zType, pType->z, pType->n);
|
||||
zType[pType->n] = 0;
|
||||
sqlite3Dequote(zType);
|
||||
pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
|
||||
pCol->colFlags |= COLFLAG_HASTYPE;
|
||||
}
|
||||
p->nCol++;
|
||||
pParse->constraintName.n = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1131,7 +1132,7 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){
|
||||
char aff = SQLITE_AFF_NUMERIC;
|
||||
const char *zChar = 0;
|
||||
|
||||
if( zIn==0 ) return aff;
|
||||
assert( zIn!=0 );
|
||||
while( zIn[0] ){
|
||||
h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
|
||||
zIn++;
|
||||
@@ -1188,28 +1189,6 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){
|
||||
return aff;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called by the parser while in the middle of
|
||||
** parsing a CREATE TABLE statement. The pFirst token is the first
|
||||
** token in the sequence of tokens that describe the type of the
|
||||
** column currently under construction. pLast is the last token
|
||||
** in the sequence. Use this information to construct a string
|
||||
** that contains the typename of the column and store that string
|
||||
** in zType.
|
||||
*/
|
||||
void sqlite3AddColumnType(Parse *pParse, Token *pType){
|
||||
Table *p;
|
||||
Column *pCol;
|
||||
|
||||
p = pParse->pNewTable;
|
||||
if( p==0 || NEVER(p->nCol<1) ) return;
|
||||
pCol = &p->aCol[p->nCol-1];
|
||||
assert( pCol->zType==0 || CORRUPT_DB );
|
||||
sqlite3DbFree(pParse->db, pCol->zType);
|
||||
pCol->zType = sqlite3NameFromToken(pParse->db, pType);
|
||||
pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst);
|
||||
}
|
||||
|
||||
/*
|
||||
** The expression is the default value for the most recently added column
|
||||
** of the table currently under construction.
|
||||
@@ -1235,11 +1214,16 @@ void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
|
||||
** tokens that point to volatile memory. The 'span' of the expression
|
||||
** is required by pragma table_info.
|
||||
*/
|
||||
Expr x;
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE);
|
||||
sqlite3DbFree(db, pCol->zDflt);
|
||||
pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
||||
(int)(pSpan->zEnd - pSpan->zStart));
|
||||
memset(&x, 0, sizeof(x));
|
||||
x.op = TK_SPAN;
|
||||
x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
||||
(int)(pSpan->zEnd - pSpan->zStart));
|
||||
x.pLeft = pSpan->pExpr;
|
||||
x.flags = EP_Skip;
|
||||
pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
|
||||
sqlite3DbFree(db, x.u.zToken);
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pSpan->pExpr);
|
||||
@@ -1295,10 +1279,10 @@ void sqlite3AddPrimaryKey(
|
||||
int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */
|
||||
){
|
||||
Table *pTab = pParse->pNewTable;
|
||||
char *zType = 0;
|
||||
Column *pCol = 0;
|
||||
int iCol = -1, i;
|
||||
int nTerm;
|
||||
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
|
||||
if( pTab==0 ) goto primary_key_exit;
|
||||
if( pTab->tabFlags & TF_HasPrimaryKey ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"table \"%s\" has more than one primary key", pTab->zName);
|
||||
@@ -1307,8 +1291,8 @@ void sqlite3AddPrimaryKey(
|
||||
pTab->tabFlags |= TF_HasPrimaryKey;
|
||||
if( pList==0 ){
|
||||
iCol = pTab->nCol - 1;
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pCol->colFlags |= COLFLAG_PRIMKEY;
|
||||
nTerm = 1;
|
||||
}else{
|
||||
nTerm = pList->nExpr;
|
||||
@@ -1320,8 +1304,8 @@ void sqlite3AddPrimaryKey(
|
||||
const char *zCName = pCExpr->u.zToken;
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pCol->colFlags |= COLFLAG_PRIMKEY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1329,7 +1313,8 @@ void sqlite3AddPrimaryKey(
|
||||
}
|
||||
}
|
||||
if( nTerm==1
|
||||
&& zType && sqlite3StrICmp(zType, "INTEGER")==0
|
||||
&& pCol
|
||||
&& sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
|
||||
&& sortOrder!=SQLITE_SO_DESC
|
||||
){
|
||||
pTab->iPKey = iCol;
|
||||
@@ -1343,12 +1328,8 @@ void sqlite3AddPrimaryKey(
|
||||
"INTEGER PRIMARY KEY");
|
||||
#endif
|
||||
}else{
|
||||
Index *p;
|
||||
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
|
||||
0, sortOrder, 0);
|
||||
if( p ){
|
||||
p->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
|
||||
}
|
||||
sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
|
||||
0, sortOrder, 0, SQLITE_IDXTYPE_PRIMARYKEY);
|
||||
pList = 0;
|
||||
}
|
||||
|
||||
@@ -1467,6 +1448,9 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
|
||||
** set back to prior value. But schema changes are infrequent
|
||||
** and the probability of hitting the same cookie value is only
|
||||
** 1 chance in 2^32. So we're safe enough.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-34230-56049 SQLite automatically increments
|
||||
** the schema-version whenever the schema changes.
|
||||
*/
|
||||
void sqlite3ChangeCookie(Parse *pParse, int iDb){
|
||||
sqlite3 *db = pParse->db;
|
||||
@@ -1608,7 +1592,7 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
|
||||
assert( pIdx->isResized==0 );
|
||||
nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
|
||||
zExtra = sqlite3DbMallocZero(db, nByte);
|
||||
if( zExtra==0 ) return SQLITE_NOMEM;
|
||||
if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
|
||||
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
|
||||
pIdx->azColl = (const char**)zExtra;
|
||||
zExtra += sizeof(char*)*N;
|
||||
@@ -1665,21 +1649,23 @@ static int hasColumn(const i16 *aiCol, int nCol, int x){
|
||||
** are appropriate for a WITHOUT ROWID table instead of a rowid table.
|
||||
** Changes include:
|
||||
**
|
||||
** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is
|
||||
** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
|
||||
** (2) Convert the OP_CreateTable into an OP_CreateIndex. There is
|
||||
** no rowid btree for a WITHOUT ROWID. Instead, the canonical
|
||||
** data storage is a covering index btree.
|
||||
** (2) Bypass the creation of the sqlite_master table entry
|
||||
** (3) Bypass the creation of the sqlite_master table entry
|
||||
** for the PRIMARY KEY as the primary key index is now
|
||||
** identified by the sqlite_master table entry of the table itself.
|
||||
** (3) Set the Index.tnum of the PRIMARY KEY Index object in the
|
||||
** (4) Set the Index.tnum of the PRIMARY KEY Index object in the
|
||||
** schema to the rootpage from the main table.
|
||||
** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
|
||||
** (5) Add all table columns to the PRIMARY KEY Index object
|
||||
** so that the PRIMARY KEY is a covering index. The surplus
|
||||
** columns are part of KeyInfo.nXField and are not used for
|
||||
** sorting or lookup or uniqueness checks.
|
||||
** (6) Replace the rowid tail on all automatically generated UNIQUE
|
||||
** indices with the PRIMARY KEY columns.
|
||||
**
|
||||
** For virtual tables, only (1) is performed.
|
||||
*/
|
||||
static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
Index *pIdx;
|
||||
@@ -1689,6 +1675,20 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
sqlite3 *db = pParse->db;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
|
||||
/* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
|
||||
*/
|
||||
if( !db->init.imposterTable ){
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){
|
||||
pTab->aCol[i].notNull = OE_Abort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The remaining transformations only apply to b-tree tables, not to
|
||||
** virtual tables */
|
||||
if( IN_DECLARE_VTAB ) return;
|
||||
|
||||
/* Convert the OP_CreateTable opcode that would normally create the
|
||||
** root-page for the table into an OP_CreateIndex opcode. The index
|
||||
** created will become the PRIMARY KEY index.
|
||||
@@ -1710,9 +1710,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
if( pList==0 ) return;
|
||||
pList->a[0].sortOrder = pParse->iPkSortOrder;
|
||||
assert( pParse->pNewTable==pTab );
|
||||
pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0);
|
||||
if( pPk==0 ) return;
|
||||
pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
|
||||
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
|
||||
SQLITE_IDXTYPE_PRIMARYKEY);
|
||||
if( db->mallocFailed ) return;
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
pTab->iPKey = -1;
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
@@ -1740,19 +1741,11 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
pPk->nKeyCol = j;
|
||||
}
|
||||
pPk->isCovering = 1;
|
||||
assert( pPk!=0 );
|
||||
pPk->isCovering = 1;
|
||||
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
|
||||
nPk = pPk->nKeyCol;
|
||||
|
||||
/* Make sure every column of the PRIMARY KEY is NOT NULL. (Except,
|
||||
** do not enforce this for imposter tables.) */
|
||||
if( !db->init.imposterTable ){
|
||||
for(i=0; i<nPk; i++){
|
||||
pTab->aCol[pPk->aiColumn[i]].notNull = OE_Abort;
|
||||
}
|
||||
pPk->uniqNotNull = 1;
|
||||
}
|
||||
|
||||
/* The root page of the PRIMARY KEY is the table root page */
|
||||
pPk->tnum = pTab->tnum;
|
||||
|
||||
@@ -1996,7 +1989,7 @@ void sqlite3EndTable(
|
||||
"UPDATE %Q.%s "
|
||||
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
|
||||
"WHERE rowid=#%d",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
zType,
|
||||
p->zName,
|
||||
p->zName,
|
||||
@@ -2011,13 +2004,13 @@ void sqlite3EndTable(
|
||||
/* Check to see if we need to create an sqlite_sequence table for
|
||||
** keeping track of autoincrement keys.
|
||||
*/
|
||||
if( p->tabFlags & TF_Autoincrement ){
|
||||
if( (p->tabFlags & TF_Autoincrement)!=0 ){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( pDb->pSchema->pSeqTab==0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
|
||||
pDb->zName
|
||||
pDb->zDbSName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2141,7 +2134,9 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
int nErr = 0; /* Number of errors encountered */
|
||||
int n; /* Temporarily holds the number of cursors assigned */
|
||||
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3_xauth xAuth; /* Saved xAuth pointer */
|
||||
#endif
|
||||
|
||||
assert( pTable );
|
||||
|
||||
@@ -2187,44 +2182,55 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
** statement that defines the view.
|
||||
*/
|
||||
assert( pTable->pSelect );
|
||||
if( pTable->pCheck ){
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
db->lookaside.bDisable++;
|
||||
sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
|
||||
&pTable->nCol, &pTable->aCol);
|
||||
db->lookaside.bDisable--;
|
||||
}else{
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
db->lookaside.bDisable++;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
db->xAuth = xAuth;
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
db->xAuth = xAuth;
|
||||
#else
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
#endif
|
||||
db->lookaside.bDisable--;
|
||||
pParse->nTab = n;
|
||||
if( pSelTab ){
|
||||
assert( pTable->aCol==0 );
|
||||
pTable->nCol = pSelTab->nCol;
|
||||
pTable->aCol = pSelTab->aCol;
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
|
||||
}else{
|
||||
pTable->nCol = 0;
|
||||
nErr++;
|
||||
pParse->nTab = n;
|
||||
if( pTable->pCheck ){
|
||||
/* CREATE VIEW name(arglist) AS ...
|
||||
** The names of the columns in the table are taken from
|
||||
** arglist which is stored in pTable->pCheck. The pCheck field
|
||||
** normally holds CHECK constraints on an ordinary table, but for
|
||||
** a VIEW it holds the list of column names.
|
||||
*/
|
||||
sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
|
||||
&pTable->nCol, &pTable->aCol);
|
||||
if( db->mallocFailed==0
|
||||
&& pParse->nErr==0
|
||||
&& pTable->nCol==pSel->pEList->nExpr
|
||||
){
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
|
||||
}
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
} else {
|
||||
}else if( pSelTab ){
|
||||
/* CREATE VIEW name AS... without an argument list. Construct
|
||||
** the column names from the SELECT statement that defines the view.
|
||||
*/
|
||||
assert( pTable->aCol==0 );
|
||||
pTable->nCol = pSelTab->nCol;
|
||||
pTable->aCol = pSelTab->aCol;
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
|
||||
}else{
|
||||
pTable->nCol = 0;
|
||||
nErr++;
|
||||
}
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
db->lookaside.bDisable--;
|
||||
} else {
|
||||
nErr++;
|
||||
}
|
||||
pTable->pSchema->schemaFlags |= DB_UnresetViews;
|
||||
#endif /* SQLITE_OMIT_VIEW */
|
||||
@@ -2320,7 +2326,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
pParse->db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
#endif
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -2396,7 +2402,7 @@ static void sqlite3ClearStatTables(
|
||||
const char *zName /* Name of index or table */
|
||||
){
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zDbSName;
|
||||
for(i=1; i<=4; i++){
|
||||
char zTab[24];
|
||||
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
|
||||
@@ -2449,7 +2455,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
pDb->zDbSName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
@@ -2463,7 +2469,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
pDb->zDbSName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
@@ -2496,6 +2502,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
assert( pName->nSrc==1 );
|
||||
if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
|
||||
if( noErr ) db->suppressErr++;
|
||||
assert( isView==0 || isView==LOCATE_VIEW );
|
||||
pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
|
||||
if( noErr ) db->suppressErr--;
|
||||
|
||||
@@ -2516,7 +2523,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
{
|
||||
int code;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zArg2 = 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
|
||||
goto exit_drop_table;
|
||||
@@ -2757,7 +2764,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -2773,6 +2780,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
tnum = pIndex->tnum;
|
||||
}
|
||||
pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
|
||||
assert( pKey!=0 || db->mallocFailed || pParse->nErr );
|
||||
|
||||
/* Open the sorter cursor if we are to use one. */
|
||||
iSorter = pParse->nTab++;
|
||||
@@ -2796,8 +2804,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
|
||||
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
|
||||
assert( pKey!=0 || db->mallocFailed || pParse->nErr );
|
||||
if( IsUniqueIndex(pIndex) && pKey!=0 ){
|
||||
if( IsUniqueIndex(pIndex) ){
|
||||
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
|
||||
sqlite3VdbeGoto(v, j2);
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
@@ -2866,12 +2873,8 @@ Index *sqlite3AllocateIndexObject(
|
||||
** pList is a list of columns to be indexed. pList will be NULL if this
|
||||
** is a primary key or unique-constraint on the most recent column added
|
||||
** to the table currently under construction.
|
||||
**
|
||||
** If the index is created successfully, return a pointer to the new Index
|
||||
** structure. This is used by sqlite3AddPrimaryKey() to mark the index
|
||||
** as the tables primary key (Index.idxType==SQLITE_IDXTYPE_PRIMARYKEY)
|
||||
*/
|
||||
Index *sqlite3CreateIndex(
|
||||
void sqlite3CreateIndex(
|
||||
Parse *pParse, /* All information about this parse */
|
||||
Token *pName1, /* First part of index name. May be NULL */
|
||||
Token *pName2, /* Second part of index name. May be NULL */
|
||||
@@ -2881,9 +2884,9 @@ Index *sqlite3CreateIndex(
|
||||
Token *pStart, /* The CREATE token that begins this statement */
|
||||
Expr *pPIWhere, /* WHERE clause for partial indices */
|
||||
int sortOrder, /* Sort order of primary key when pList==NULL */
|
||||
int ifNotExist /* Omit error if index already exists */
|
||||
int ifNotExist, /* Omit error if index already exists */
|
||||
u8 idxType /* The index type */
|
||||
){
|
||||
Index *pRet = 0; /* Pointer to return */
|
||||
Table *pTab = 0; /* Table to be indexed */
|
||||
Index *pIndex = 0; /* The index to be created */
|
||||
char *zName = 0; /* Name of the index */
|
||||
@@ -2901,7 +2904,10 @@ Index *sqlite3CreateIndex(
|
||||
char *zExtra = 0; /* Extra space after the Index object */
|
||||
Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */
|
||||
|
||||
if( db->mallocFailed || IN_DECLARE_VTAB || pParse->nErr>0 ){
|
||||
if( db->mallocFailed || pParse->nErr>0 ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
@@ -3010,7 +3016,7 @@ Index *sqlite3CreateIndex(
|
||||
goto exit_create_index;
|
||||
}
|
||||
}
|
||||
if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
|
||||
if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
|
||||
if( !ifNotExist ){
|
||||
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
|
||||
}else{
|
||||
@@ -3027,13 +3033,20 @@ Index *sqlite3CreateIndex(
|
||||
if( zName==0 ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
||||
/* Automatic index names generated from within sqlite3_declare_vtab()
|
||||
** must have names that are distinct from normal automatic index names.
|
||||
** The following statement converts "sqlite3_autoindex..." into
|
||||
** "sqlite3_butoindex..." in order to make the names distinct.
|
||||
** The "vtab_err.test" test demonstrates the need of this statement. */
|
||||
if( IN_DECLARE_VTAB ) zName[7]++;
|
||||
}
|
||||
|
||||
/* Check for authorization to create an index.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
const char *zDb = pDb->zName;
|
||||
const char *zDb = pDb->zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
@@ -3090,7 +3103,7 @@ Index *sqlite3CreateIndex(
|
||||
pIndex->pTable = pTab;
|
||||
pIndex->onError = (u8)onError;
|
||||
pIndex->uniqNotNull = onError!=OE_None;
|
||||
pIndex->idxType = pName ? SQLITE_IDXTYPE_APPDEF : SQLITE_IDXTYPE_UNIQUE;
|
||||
pIndex->idxType = idxType;
|
||||
pIndex->pSchema = db->aDb[iDb].pSchema;
|
||||
pIndex->nKeyCol = pList->nExpr;
|
||||
if( pPIWhere ){
|
||||
@@ -3200,6 +3213,20 @@ Index *sqlite3CreateIndex(
|
||||
sqlite3DefaultRowEst(pIndex);
|
||||
if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
|
||||
|
||||
/* If this index contains every column of its table, then mark
|
||||
** it as a covering index */
|
||||
assert( HasRowid(pTab)
|
||||
|| pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
|
||||
if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
|
||||
pIndex->isCovering = 1;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( j==pTab->iPKey ) continue;
|
||||
if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue;
|
||||
pIndex->isCovering = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( pTab==pParse->pNewTable ){
|
||||
/* This routine has been called to create an automatic index as a
|
||||
** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
|
||||
@@ -3237,7 +3264,7 @@ Index *sqlite3CreateIndex(
|
||||
if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
|
||||
z1 = pIdx->azColl[k];
|
||||
z2 = pIndex->azColl[k];
|
||||
if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
|
||||
if( sqlite3StrICmp(z1, z2) ) break;
|
||||
}
|
||||
if( k==pIdx->nKeyCol ){
|
||||
if( pIdx->onError!=pIndex->onError ){
|
||||
@@ -3256,7 +3283,7 @@ Index *sqlite3CreateIndex(
|
||||
pIdx->onError = pIndex->onError;
|
||||
}
|
||||
}
|
||||
pRet = pIdx;
|
||||
if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType;
|
||||
goto exit_create_index;
|
||||
}
|
||||
}
|
||||
@@ -3268,6 +3295,7 @@ Index *sqlite3CreateIndex(
|
||||
assert( pParse->nErr==0 );
|
||||
if( db->init.busy ){
|
||||
Index *p;
|
||||
assert( !IN_DECLARE_VTAB );
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
|
||||
p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
|
||||
pIndex->zName, pIndex);
|
||||
@@ -3333,7 +3361,7 @@ Index *sqlite3CreateIndex(
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
pIndex->zName,
|
||||
pTab->zName,
|
||||
iMem,
|
||||
@@ -3349,7 +3377,7 @@ Index *sqlite3CreateIndex(
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3VdbeAddOp1(v, OP_Expire, 0);
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
}
|
||||
|
||||
sqlite3VdbeJumpHere(v, pIndex->tnum);
|
||||
@@ -3374,7 +3402,6 @@ Index *sqlite3CreateIndex(
|
||||
pIndex->pNext = pOther->pNext;
|
||||
pOther->pNext = pIndex;
|
||||
}
|
||||
pRet = pIndex;
|
||||
pIndex = 0;
|
||||
}
|
||||
|
||||
@@ -3385,7 +3412,6 @@ exit_create_index:
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3SrcListDelete(db, pTblName);
|
||||
sqlite3DbFree(db, zName);
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3414,10 +3440,11 @@ void sqlite3DefaultRowEst(Index *pIdx){
|
||||
int i;
|
||||
|
||||
/* Set the first entry (number of rows in the index) to the estimated
|
||||
** number of rows in the table. Or 10, if the estimated number of rows
|
||||
** in the table is less than that. */
|
||||
** number of rows in the table, or half the number of rows in the table
|
||||
** for a partial index. But do not let the estimate drop below 10. */
|
||||
a[0] = pIdx->pTable->nRowLogEst;
|
||||
if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
|
||||
if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) );
|
||||
if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
|
||||
|
||||
/* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
|
||||
** 6 and each subsequent value (if any) is 5. */
|
||||
@@ -3468,7 +3495,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
{
|
||||
int code = SQLITE_DROP_INDEX;
|
||||
Table *pTab = pIndex->pTable;
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
|
||||
goto exit_drop_index;
|
||||
@@ -3486,7 +3513,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
);
|
||||
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@@ -4007,15 +4034,13 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
*/
|
||||
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
sqlite3 *db = pToplevel->db;
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
assert( iDb>=0 && iDb<pParse->db->nDb );
|
||||
assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
assert( iDb<SQLITE_MAX_ATTACHED+2 );
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
|
||||
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
|
||||
DbMaskSet(pToplevel->cookieMask, iDb);
|
||||
pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
sqlite3OpenTempDatabase(pToplevel);
|
||||
}
|
||||
@@ -4031,7 +4056,7 @@ void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zDbSName)) ){
|
||||
sqlite3CodeVerifySchema(pParse, i);
|
||||
}
|
||||
}
|
||||
@@ -4278,7 +4303,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
if( iDb<0 ) return;
|
||||
z = sqlite3NameFromToken(db, pObjName);
|
||||
if( z==0 ) return;
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
pTab = sqlite3FindTable(db, z, zDb);
|
||||
if( pTab ){
|
||||
reindexTable(pParse, pTab, 0);
|
||||
@@ -4299,10 +4324,6 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
/*
|
||||
** Return a KeyInfo structure that is appropriate for the given Index.
|
||||
**
|
||||
** The KeyInfo structure for an index is cached in the Index object.
|
||||
** So there might be multiple references to the returned pointer. The
|
||||
** caller should not try to modify the KeyInfo object.
|
||||
**
|
||||
** The caller should invoke sqlite3KeyInfoUnref() on the returned object
|
||||
** when it has finished using it.
|
||||
*/
|
||||
|
||||
+39
-31
@@ -284,14 +284,12 @@ static int matchQuality(
|
||||
** a pointer to the matching FuncDef if found, or 0 if there is no match.
|
||||
*/
|
||||
static FuncDef *functionSearch(
|
||||
FuncDefHash *pHash, /* Hash table to search */
|
||||
int h, /* Hash of the name */
|
||||
const char *zFunc, /* Name of function */
|
||||
int nFunc /* Number of bytes in zFunc */
|
||||
const char *zFunc /* Name of function */
|
||||
){
|
||||
FuncDef *p;
|
||||
for(p=pHash->a[h]; p; p=p->pHash){
|
||||
if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){
|
||||
for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
|
||||
if( sqlite3StrICmp(p->zName, zFunc)==0 ){
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@@ -301,23 +299,26 @@ static FuncDef *functionSearch(
|
||||
/*
|
||||
** Insert a new FuncDef into a FuncDefHash hash table.
|
||||
*/
|
||||
void sqlite3FuncDefInsert(
|
||||
FuncDefHash *pHash, /* The hash table into which to insert */
|
||||
FuncDef *pDef /* The function definition to insert */
|
||||
void sqlite3InsertBuiltinFuncs(
|
||||
FuncDef *aDef, /* List of global functions to be inserted */
|
||||
int nDef /* Length of the apDef[] list */
|
||||
){
|
||||
FuncDef *pOther;
|
||||
int nName = sqlite3Strlen30(pDef->zName);
|
||||
u8 c1 = (u8)pDef->zName[0];
|
||||
int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a);
|
||||
pOther = functionSearch(pHash, h, pDef->zName, nName);
|
||||
if( pOther ){
|
||||
assert( pOther!=pDef && pOther->pNext!=pDef );
|
||||
pDef->pNext = pOther->pNext;
|
||||
pOther->pNext = pDef;
|
||||
}else{
|
||||
pDef->pNext = 0;
|
||||
pDef->pHash = pHash->a[h];
|
||||
pHash->a[h] = pDef;
|
||||
int i;
|
||||
for(i=0; i<nDef; i++){
|
||||
FuncDef *pOther;
|
||||
const char *zName = aDef[i].zName;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
|
||||
pOther = functionSearch(h, zName);
|
||||
if( pOther ){
|
||||
assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] );
|
||||
aDef[i].pNext = pOther->pNext;
|
||||
pOther->pNext = &aDef[i];
|
||||
}else{
|
||||
aDef[i].pNext = 0;
|
||||
aDef[i].u.pHash = sqlite3BuiltinFunctions.a[h];
|
||||
sqlite3BuiltinFunctions.a[h] = &aDef[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,8 +345,7 @@ void sqlite3FuncDefInsert(
|
||||
*/
|
||||
FuncDef *sqlite3FindFunction(
|
||||
sqlite3 *db, /* An open database */
|
||||
const char *zName, /* Name of the function. Not null-terminated */
|
||||
int nName, /* Number of characters in the name */
|
||||
const char *zName, /* Name of the function. zero-terminated */
|
||||
int nArg, /* Number of arguments. -1 means any number */
|
||||
u8 enc, /* Preferred text encoding */
|
||||
u8 createFlag /* Create new entry if true and does not otherwise exist */
|
||||
@@ -354,14 +354,15 @@ FuncDef *sqlite3FindFunction(
|
||||
FuncDef *pBest = 0; /* Best match found so far */
|
||||
int bestScore = 0; /* Score of best match */
|
||||
int h; /* Hash value */
|
||||
int nName; /* Length of the name */
|
||||
|
||||
assert( nArg>=(-2) );
|
||||
assert( nArg>=(-1) || createFlag==0 );
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
|
||||
nName = sqlite3Strlen30(zName);
|
||||
|
||||
/* First search for a match amongst the application-defined functions.
|
||||
*/
|
||||
p = functionSearch(&db->aFunc, h, zName, nName);
|
||||
p = (FuncDef*)sqlite3HashFind(&db->aFunc, zName);
|
||||
while( p ){
|
||||
int score = matchQuality(p, nArg, enc);
|
||||
if( score>bestScore ){
|
||||
@@ -384,9 +385,9 @@ FuncDef *sqlite3FindFunction(
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
*/
|
||||
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
bestScore = 0;
|
||||
p = functionSearch(pHash, h, zName, nName);
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
|
||||
p = functionSearch(h, zName);
|
||||
while( p ){
|
||||
int score = matchQuality(p, nArg, enc);
|
||||
if( score>bestScore ){
|
||||
@@ -403,12 +404,19 @@ FuncDef *sqlite3FindFunction(
|
||||
*/
|
||||
if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
|
||||
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
||||
pBest->zName = (char *)&pBest[1];
|
||||
FuncDef *pOther;
|
||||
pBest->zName = (const char*)&pBest[1];
|
||||
pBest->nArg = (u16)nArg;
|
||||
pBest->funcFlags = enc;
|
||||
memcpy(pBest->zName, zName, nName);
|
||||
pBest->zName[nName] = 0;
|
||||
sqlite3FuncDefInsert(&db->aFunc, pBest);
|
||||
memcpy((char*)&pBest[1], zName, nName+1);
|
||||
pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
|
||||
if( pOther==pBest ){
|
||||
sqlite3DbFree(db, pBest);
|
||||
sqlite3OomFault(db);
|
||||
return 0;
|
||||
}else{
|
||||
pBest->pNext = pOther;
|
||||
}
|
||||
}
|
||||
|
||||
if( pBest && (pBest->xSFunc || createFlag) ){
|
||||
|
||||
+1
-1
@@ -281,7 +281,7 @@ int sqlite3_complete16(const void *zSql){
|
||||
if( zSql8 ){
|
||||
rc = sqlite3_complete(zSql8);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
return rc & 0xff;
|
||||
|
||||
+2
-2
@@ -402,7 +402,7 @@ static int sqlcipher_find_db_index(sqlite3 *db, const char *zDb) {
|
||||
}
|
||||
for(db_index = 0; db_index < db->nDb; db_index++) {
|
||||
struct Db *pDb = &db->aDb[db_index];
|
||||
if(strcmp(pDb->zName, zDb) == 0) {
|
||||
if(strcmp(pDb->zDbSName, zDb) == 0) {
|
||||
return db_index;
|
||||
}
|
||||
}
|
||||
@@ -610,7 +610,7 @@ void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **ar
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
int saved_nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
||||
void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
|
||||
int (*saved_xTrace)(u32,void*,void*,void*); /* Saved db->xTrace */
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
char *zSql = NULL; /* SQL statements */
|
||||
char *pzErrMsg = NULL;
|
||||
|
||||
+1
-1
@@ -1022,7 +1022,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
int saved_flags;
|
||||
int saved_nChange;
|
||||
int saved_nTotalChange;
|
||||
void (*saved_xTrace)(void*,const char*);
|
||||
int (*saved_xTrace)(u32,void*,void*,void*); /* Saved db->xTrace */
|
||||
Db *pDb = 0;
|
||||
sqlite3 *db = ctx->pBt->db;
|
||||
const char *db_filename = sqlite3_db_filename(db, "main");
|
||||
|
||||
+10
-1
@@ -45,6 +45,15 @@ static const char * const azCompileOpt[] = {
|
||||
#if SQLITE_CHECK_PAGES
|
||||
"CHECK_PAGES",
|
||||
#endif
|
||||
#if defined(__clang__) && defined(__clang_major__)
|
||||
"COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "."
|
||||
CTIMEOPT_VAL(__clang_minor__) "."
|
||||
CTIMEOPT_VAL(__clang_patchlevel__),
|
||||
#elif defined(_MSC_VER)
|
||||
"COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
|
||||
#elif defined(__GNUC__) && defined(__VERSION__)
|
||||
"COMPILER=gcc-" __VERSION__,
|
||||
#endif
|
||||
#if SQLITE_COVERAGE_TEST
|
||||
"COVERAGE_TEST",
|
||||
#endif
|
||||
@@ -64,7 +73,7 @@ static const char * const azCompileOpt[] = {
|
||||
"DISABLE_LFS",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_8_3_NAMES
|
||||
"ENABLE_8_3_NAMES",
|
||||
"ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES),
|
||||
#endif
|
||||
#if SQLITE_ENABLE_API_ARMOR
|
||||
"ENABLE_API_ARMOR",
|
||||
|
||||
+12
-10
@@ -50,6 +50,15 @@
|
||||
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
|
||||
/*
|
||||
** The MSVC CRT on Windows CE may not have a localtime() function.
|
||||
** So declare a substitute. The substitute function itself is
|
||||
** defined in "os_win.c".
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
|
||||
(!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
|
||||
struct tm *__cdecl localtime(const time_t *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** A structure for holding a single date and time.
|
||||
@@ -418,6 +427,7 @@ static void clearYMD_HMS_TZ(DateTime *p){
|
||||
p->validTZ = 0;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
/*
|
||||
** On recent Windows platforms, the localtime_s() function is available
|
||||
** as part of the "Secure CRT". It is essentially equivalent to
|
||||
@@ -436,7 +446,6 @@ static void clearYMD_HMS_TZ(DateTime *p){
|
||||
#define HAVE_LOCALTIME_S 1
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
/*
|
||||
** The following routine implements the rough equivalent of localtime_r()
|
||||
** using whatever operating-system specific localtime facility that
|
||||
@@ -1103,7 +1112,6 @@ static void currentTimeFunc(
|
||||
){
|
||||
time_t t;
|
||||
char *zFormat = (char *)sqlite3_user_data(context);
|
||||
sqlite3 *db;
|
||||
sqlite3_int64 iT;
|
||||
struct tm *pTm;
|
||||
struct tm sNow;
|
||||
@@ -1136,7 +1144,7 @@ static void currentTimeFunc(
|
||||
** external linkage.
|
||||
*/
|
||||
void sqlite3RegisterDateTimeFunctions(void){
|
||||
static SQLITE_WSD FuncDef aDateTimeFuncs[] = {
|
||||
static FuncDef aDateTimeFuncs[] = {
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
DFUNCTION(julianday, -1, 0, 0, juliandayFunc ),
|
||||
DFUNCTION(date, -1, 0, 0, dateFunc ),
|
||||
@@ -1152,11 +1160,5 @@ void sqlite3RegisterDateTimeFunctions(void){
|
||||
STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs);
|
||||
|
||||
for(i=0; i<ArraySize(aDateTimeFuncs); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs));
|
||||
}
|
||||
|
||||
+15
-15
@@ -58,10 +58,10 @@
|
||||
*/
|
||||
#define VTAB_SCHEMA \
|
||||
"CREATE TABLE xx( " \
|
||||
" name STRING, /* Name of table or index */" \
|
||||
" path INTEGER, /* Path to page from root */" \
|
||||
" name TEXT, /* Name of table or index */" \
|
||||
" path TEXT, /* Path to page from root */" \
|
||||
" pageno INTEGER, /* Page number */" \
|
||||
" pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \
|
||||
" pagetype TEXT, /* 'internal', 'leaf' or 'overflow' */" \
|
||||
" ncell INTEGER, /* Cells on page (0 for overflow) */" \
|
||||
" payload INTEGER, /* Bytes of payload on this page */" \
|
||||
" unused INTEGER, /* Bytes of unused space on this page */" \
|
||||
@@ -162,7 +162,7 @@ static int statConnect(
|
||||
rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM;
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
assert( rc==SQLITE_OK || pTab==0 );
|
||||
@@ -243,7 +243,7 @@ static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
|
||||
pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
memset(pCsr, 0, sizeof(StatCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
@@ -349,7 +349,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell));
|
||||
if( p->aCell==0 ) return SQLITE_NOMEM;
|
||||
if( p->aCell==0 ) return SQLITE_NOMEM_BKPT;
|
||||
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
|
||||
|
||||
for(i=0; i<p->nCell; i++){
|
||||
@@ -382,7 +382,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
|
||||
pCell->nOvfl = nOvfl;
|
||||
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
|
||||
if( pCell->aOvfl==0 ) return SQLITE_NOMEM;
|
||||
if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]);
|
||||
for(j=1; j<nOvfl; j++){
|
||||
int rc;
|
||||
@@ -461,7 +461,7 @@ statNextRestart:
|
||||
pCsr->aPage[0].iCell = 0;
|
||||
pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
|
||||
pCsr->iPage = 0;
|
||||
if( z==0 ) rc = SQLITE_NOMEM;
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pCsr->isEof = 1;
|
||||
return sqlite3_reset(pCsr->pStmt);
|
||||
@@ -496,7 +496,7 @@ statNextRestart:
|
||||
}
|
||||
pCell->iOvfl++;
|
||||
statSizeAndOffset(pCsr);
|
||||
return z==0 ? SQLITE_NOMEM : SQLITE_OK;
|
||||
return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
|
||||
}
|
||||
if( p->iRightChildPg ) break;
|
||||
p->iCell++;
|
||||
@@ -520,7 +520,7 @@ statNextRestart:
|
||||
p[1].iCell = 0;
|
||||
p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
|
||||
p->iCell++;
|
||||
if( z==0 ) rc = SQLITE_NOMEM;
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
|
||||
@@ -554,7 +554,7 @@ statNextRestart:
|
||||
pCsr->nUnused = p->nUnused;
|
||||
pCsr->nMxPayload = p->nMxPayload;
|
||||
pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
|
||||
if( z==0 ) rc = SQLITE_NOMEM;
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
nPayload = 0;
|
||||
for(i=0; i<p->nCell; i++){
|
||||
nPayload += p->aCell[i].nLocal;
|
||||
@@ -588,7 +588,7 @@ static int statFilter(
|
||||
if( pCsr->iDb<0 ){
|
||||
sqlite3_free(pCursor->pVtab->zErrMsg);
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
|
||||
return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
|
||||
return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}else{
|
||||
pCsr->iDb = pTab->iDb;
|
||||
@@ -602,9 +602,9 @@ static int statFilter(
|
||||
" UNION ALL "
|
||||
"SELECT name, rootpage, type"
|
||||
" FROM \"%w\".%s WHERE rootpage!=0"
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
@@ -656,7 +656,7 @@ static int statColumn(
|
||||
default: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int iDb = pCsr->iDb;
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-29
@@ -102,7 +102,7 @@ void sqlite3MaterializeView(
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
@@ -143,7 +143,7 @@ Expr *sqlite3LimitWhere(
|
||||
*/
|
||||
if( pOrderBy && (pLimit == 0) ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
goto limit_where_cleanup_2;
|
||||
goto limit_where_cleanup;
|
||||
}
|
||||
|
||||
/* We only need to generate a select expression if there
|
||||
@@ -165,16 +165,16 @@ Expr *sqlite3LimitWhere(
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup;
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
|
||||
if( pEList == 0 ) goto limit_where_cleanup_2;
|
||||
if( pEList == 0 ) goto limit_where_cleanup;
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup_2;
|
||||
goto limit_where_cleanup;
|
||||
}
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
@@ -184,21 +184,11 @@ Expr *sqlite3LimitWhere(
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
|
||||
if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
|
||||
pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
|
||||
if( pInClause == 0 ) goto limit_where_cleanup_1;
|
||||
|
||||
pInClause->x.pSelect = pSelect;
|
||||
pInClause->flags |= EP_xIsSelect;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, pInClause);
|
||||
pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0) : 0;
|
||||
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
|
||||
return pInClause;
|
||||
|
||||
/* something went wrong. clean up anything allocated. */
|
||||
limit_where_cleanup_1:
|
||||
sqlite3SelectDelete(pParse->db, pSelect);
|
||||
return 0;
|
||||
|
||||
limit_where_cleanup_2:
|
||||
limit_where_cleanup:
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse->db, pLimit);
|
||||
@@ -222,7 +212,6 @@ void sqlite3DeleteFrom(
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
const char *zDb; /* Name of database holding pTab */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
@@ -249,11 +238,12 @@ void sqlite3DeleteFrom(
|
||||
int addrBypass = 0; /* Address of jump over the delete logic */
|
||||
int addrLoop = 0; /* Top of the delete loop */
|
||||
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
|
||||
int bComplex; /* True if there are triggers or FKs or
|
||||
** subqueries in the WHERE clause */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
Trigger *pTrigger; /* List of table triggers, if required */
|
||||
int bComplex; /* True if there are either triggers or FKs */
|
||||
#endif
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
@@ -281,7 +271,6 @@ void sqlite3DeleteFrom(
|
||||
#else
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
# define bComplex 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
@@ -299,8 +288,8 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
zDb = db->aDb[iDb].zName;
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
|
||||
db->aDb[iDb].zDbSName);
|
||||
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
|
||||
if( rcauth==SQLITE_DENY ){
|
||||
goto delete_from_cleanup;
|
||||
@@ -366,6 +355,9 @@ void sqlite3DeleteFrom(
|
||||
&& pWhere==0
|
||||
&& !bComplex
|
||||
&& !IsVirtual(pTab)
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
&& db->xPreUpdateCallback==0
|
||||
#endif
|
||||
){
|
||||
assert( !isView );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
@@ -380,7 +372,8 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
{
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
|
||||
if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
|
||||
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||
if( HasRowid(pTab) ){
|
||||
/* For a rowid table, initialize the RowSet to an empty set */
|
||||
@@ -480,7 +473,7 @@ void sqlite3DeleteFrom(
|
||||
if( !isView ){
|
||||
int iAddrOnce = 0;
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
testcase( IsVirtual(pTab) );
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
|
||||
@@ -715,14 +708,19 @@ void sqlite3GenerateRowDelete(
|
||||
|
||||
/* Delete the index and table entries. Skip this step if pTab is really
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
** fire the INSTEAD OF triggers).
|
||||
**
|
||||
** If variable 'count' is non-zero, then this OP_Delete instruction should
|
||||
** invoke the update-hook. The pre-update-hook, on the other hand should
|
||||
** be invoked unless table pTab is a system table. The difference is that
|
||||
** the update-hook is not invoked for rows removed by REPLACE, but the
|
||||
** pre-update-hook is.
|
||||
*/
|
||||
if( pTab->pSelect==0 ){
|
||||
u8 p5 = 0;
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
|
||||
if( eMode!=ONEPASS_OFF ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);
|
||||
}
|
||||
|
||||
+1285
-528
File diff suppressed because it is too large
Load Diff
+8
-4
@@ -871,7 +871,7 @@ void sqlite3FkCheck(
|
||||
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
|
||||
/* Loop through all the foreign key constraints for which pTab is the
|
||||
** child table (the table that the foreign key definition is part of). */
|
||||
@@ -1162,6 +1162,9 @@ static Trigger *fkActionTrigger(
|
||||
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
|
||||
|
||||
action = pFKey->aAction[iAction];
|
||||
if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
|
||||
return 0;
|
||||
}
|
||||
pTrigger = pFKey->apTrigger[iAction];
|
||||
|
||||
if( action!=OE_None && !pTrigger ){
|
||||
@@ -1239,10 +1242,10 @@ static Trigger *fkActionTrigger(
|
||||
if( pDflt ){
|
||||
pNew = sqlite3ExprDup(db, pDflt, 0);
|
||||
}else{
|
||||
pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
|
||||
pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
|
||||
}
|
||||
}else{
|
||||
pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
|
||||
pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
|
||||
}
|
||||
pList = sqlite3ExprListAppend(pParse, pList, pNew);
|
||||
sqlite3ExprListSetName(pParse, pList, &tFromCol, 0);
|
||||
@@ -1369,7 +1372,8 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){
|
||||
FKey *pFKey; /* Iterator variable */
|
||||
FKey *pNext; /* Copy of pFKey->pNextFrom */
|
||||
|
||||
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
|
||||
assert( db==0 || IsVirtual(pTab)
|
||||
|| sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
|
||||
for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
|
||||
|
||||
/* Remove the FK from the fkeyHash hash table. */
|
||||
|
||||
+84
-43
@@ -208,6 +208,8 @@ static void instrFunc(
|
||||
zHaystack = sqlite3_value_text(argv[0]);
|
||||
zNeedle = sqlite3_value_text(argv[1]);
|
||||
isText = 1;
|
||||
if( zNeedle==0 ) return;
|
||||
assert( zHaystack );
|
||||
}
|
||||
while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){
|
||||
N++;
|
||||
@@ -740,7 +742,7 @@ static int patternCompare(
|
||||
}
|
||||
c2 = Utf8Read(zString);
|
||||
if( c==c2 ) continue;
|
||||
if( noCase && c<0x80 && c2<0x80 && sqlite3Tolower(c)==sqlite3Tolower(c2) ){
|
||||
if( noCase && sqlite3Tolower(c)==sqlite3Tolower(c2) && c<0x80 && c2<0x80 ){
|
||||
continue;
|
||||
}
|
||||
if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue;
|
||||
@@ -1316,6 +1318,26 @@ static void trimFunc(
|
||||
}
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
/*
|
||||
** The "unknown" function is automatically substituted in place of
|
||||
** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN
|
||||
** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used.
|
||||
** When the "sqlite3" command-line shell is built using this functionality,
|
||||
** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries
|
||||
** involving application-defined functions to be examined in a generic
|
||||
** sqlite3 shell.
|
||||
*/
|
||||
static void unknownFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
/* no-op */
|
||||
}
|
||||
#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
|
||||
|
||||
|
||||
/* IMP: R-25361-16150 This function is omitted from SQLite by default. It
|
||||
** is only available if the SQLITE_SOUNDEX compile-time option is used
|
||||
** when SQLite is built.
|
||||
@@ -1386,6 +1408,14 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
char *zErrMsg = 0;
|
||||
|
||||
/* Disallow the load_extension() SQL function unless the SQLITE_LoadExtFunc
|
||||
** flag is set. See the sqlite3_enable_load_extension() API.
|
||||
*/
|
||||
if( (db->flags & SQLITE_LoadExtFunc)==0 ){
|
||||
sqlite3_result_error(context, "not authorized", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if( argc==2 ){
|
||||
zProc = (const char *)sqlite3_value_text(argv[1]);
|
||||
}else{
|
||||
@@ -1611,7 +1641,7 @@ static void groupConcatFinalize(sqlite3_context *context){
|
||||
** of the built-in functions above are part of the global function set.
|
||||
** This routine only deals with those that are not global.
|
||||
*/
|
||||
void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
|
||||
int rc = sqlite3_overload_function(db, "MATCH", 2);
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
@@ -1638,8 +1668,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
*/
|
||||
static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
|
||||
FuncDef *pDef;
|
||||
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
|
||||
2, SQLITE_UTF8, 0);
|
||||
pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0);
|
||||
if( ALWAYS(pDef) ){
|
||||
pDef->funcFlags |= flagVal;
|
||||
}
|
||||
@@ -1687,9 +1716,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken,
|
||||
sqlite3Strlen30(pExpr->u.zToken),
|
||||
2, SQLITE_UTF8, 0);
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
|
||||
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
@@ -1713,7 +1740,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
**
|
||||
** After this routine runs
|
||||
*/
|
||||
void sqlite3RegisterGlobalFunctions(void){
|
||||
void sqlite3RegisterBuiltinFunctions(void){
|
||||
/*
|
||||
** The following array holds FuncDef structures for all of the functions
|
||||
** defined in this file.
|
||||
@@ -1721,8 +1748,27 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
** The array cannot be constant since changes are made to the
|
||||
** FuncDef.pHash elements at start-time. The elements of this array
|
||||
** are read-only after initialization is complete.
|
||||
**
|
||||
** For peak efficiency, put the most frequently used function last.
|
||||
*/
|
||||
static SQLITE_WSD FuncDef aBuiltinFunc[] = {
|
||||
static FuncDef aBuiltinFunc[] = {
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
VFUNCTION(load_extension, 1, 0, 0, loadExt ),
|
||||
VFUNCTION(load_extension, 2, 0, 0, loadExt ),
|
||||
#endif
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
|
||||
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
|
||||
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
|
||||
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
|
||||
FUNCTION(rtrim, 1, 2, 0, trimFunc ),
|
||||
@@ -1740,8 +1786,6 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
||||
FUNCTION(instr, 2, 0, 0, instrFunc ),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(printf, -1, 0, 0, printfFunc ),
|
||||
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
|
||||
FUNCTION(char, -1, 0, 0, charFunc ),
|
||||
@@ -1752,40 +1796,22 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
#endif
|
||||
FUNCTION(upper, 1, 0, 0, upperFunc ),
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
VFUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
|
||||
DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
|
||||
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
|
||||
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
|
||||
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
FUNCTION(quote, 1, 0, 0, quoteFunc ),
|
||||
VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
|
||||
VFUNCTION(changes, 0, 0, 0, changes ),
|
||||
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
|
||||
FUNCTION(replace, 3, 0, 0, replaceFunc ),
|
||||
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
VFUNCTION(load_extension, 1, 0, 0, loadExt ),
|
||||
VFUNCTION(load_extension, 2, 0, 0, loadExt ),
|
||||
#endif
|
||||
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 ),
|
||||
@@ -1796,27 +1822,42 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
||||
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#else
|
||||
#else
|
||||
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
FUNCTION(unknown, -1, 0, 0, unknownFunc ),
|
||||
#endif
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
};
|
||||
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc);
|
||||
|
||||
for(i=0; i<ArraySize(aBuiltinFunc); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
sqlite3RegisterDateTimeFunctions();
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
sqlite3RegisterDateTimeFunctions();
|
||||
sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
|
||||
|
||||
#if 0 /* Enable to print out how the built-in functions are hashed */
|
||||
{
|
||||
int i;
|
||||
FuncDef *p;
|
||||
for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
|
||||
printf("FUNC-HASH %02d:", i);
|
||||
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){
|
||||
int n = sqlite3Strlen30(p->zName);
|
||||
int h = p->zName[0] + n;
|
||||
printf(" %s(%d)", p->zName, h);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+23
-11
@@ -70,6 +70,7 @@ const unsigned char sqlite3UpperToLower[] = {
|
||||
** isxdigit() 0x08
|
||||
** toupper() 0x20
|
||||
** SQLite identifier character 0x40
|
||||
** Quote character 0x80
|
||||
**
|
||||
** Bit 0x20 is set if the mapped character requires translation to upper
|
||||
** case. i.e. if the character is a lower-case ASCII character.
|
||||
@@ -78,16 +79,13 @@ const unsigned char sqlite3UpperToLower[] = {
|
||||
**
|
||||
** (x & ~(map[x]&0x20))
|
||||
**
|
||||
** Standard function tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** array. tolower() is used more often than toupper() by SQLite.
|
||||
**
|
||||
** Bit 0x40 is set if the character non-alphanumeric and can be used in an
|
||||
** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
|
||||
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
|
||||
** non-ASCII UTF character. Hence the test for whether or not a character is
|
||||
** part of an identifier is 0x46.
|
||||
**
|
||||
** SQLite's versions are identical to the standard versions assuming a
|
||||
** locale of "C". They are implemented as macros in sqliteInt.h.
|
||||
*/
|
||||
#ifdef SQLITE_ASCII
|
||||
const unsigned char sqlite3CtypeMap[256] = {
|
||||
@@ -95,7 +93,7 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
|
||||
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */
|
||||
0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
|
||||
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
|
||||
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
|
||||
@@ -103,8 +101,8 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
|
||||
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
|
||||
0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
|
||||
0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
|
||||
0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
|
||||
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
|
||||
@@ -159,6 +157,18 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
# define SQLITE_SORTER_PMASZ 250
|
||||
#endif
|
||||
|
||||
/* Statement journals spill to disk when their size exceeds the following
|
||||
** threshold (in bytes). 0 means that statement journals are created and
|
||||
** written to disk immediately (the default behavior for SQLite versions
|
||||
** before 3.12.0). -1 means always keep the entire statement journal in
|
||||
** memory. (The statement journal is also always held entirely in memory
|
||||
** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this
|
||||
** setting.)
|
||||
*/
|
||||
#ifndef SQLITE_STMTJRNL_SPILL
|
||||
# define SQLITE_STMTJRNL_SPILL (64*1024)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following singleton contains the global configuration for
|
||||
** the SQLite library.
|
||||
@@ -173,6 +183,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* neverCorrupt */
|
||||
128, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
|
||||
{0,0,0,0,0,0,0,0}, /* m */
|
||||
{0,0,0,0,0,0,0,0,0}, /* mutex */
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
|
||||
@@ -211,7 +222,8 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
0, /* xTestCallback */
|
||||
#endif
|
||||
0 /* bLocaltimeFault */
|
||||
0, /* bLocaltimeFault */
|
||||
0x7ffffffe /* iOnceResetThreshold */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -219,7 +231,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
** database connections. After initialization, this table is
|
||||
** read-only.
|
||||
*/
|
||||
SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
|
||||
FuncDefHash sqlite3BuiltinFunctions;
|
||||
|
||||
/*
|
||||
** Constant tokens for values 0 and 1.
|
||||
@@ -234,7 +246,7 @@ const Token sqlite3IntTokens[] = {
|
||||
** The value of the "pending" byte must be 0x40000000 (1 byte past the
|
||||
** 1-gibabyte boundary) in a compatible database. SQLite never uses
|
||||
** the database page that contains the pending byte. It never attempts
|
||||
** to read or write that page. The pending byte page is set assign
|
||||
** to read or write that page. The pending byte page is set aside
|
||||
** for use by the VFS layers as space for managing file locks.
|
||||
**
|
||||
** During testing, it is often desirable to move the pending byte to
|
||||
|
||||
+7
-3
@@ -55,8 +55,12 @@ void sqlite3HashClear(Hash *pH){
|
||||
static unsigned int strHash(const char *z){
|
||||
unsigned int h = 0;
|
||||
unsigned char c;
|
||||
while( (c = (unsigned char)*z++)!=0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[c];
|
||||
while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
/* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
|
||||
** 0x9e3779b1 is 2654435761 which is the closest prime number to
|
||||
** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
|
||||
h += sqlite3UpperToLower[c];
|
||||
h *= 0x9e3779b1;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -148,7 +152,7 @@ static HashElem *findElementWithHash(
|
||||
int count; /* Number of elements left to test */
|
||||
unsigned int h; /* The computed hash */
|
||||
|
||||
if( pH->ht ){
|
||||
if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
struct _ht *pEntry;
|
||||
h = strHash(pKey) % pH->htsize;
|
||||
pEntry = &pH->ht[h];
|
||||
|
||||
+3
-3
@@ -12,8 +12,8 @@
|
||||
** This is the header file for the generic hash-table implementation
|
||||
** used in SQLite.
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
#ifndef SQLITE_HASH_H
|
||||
#define SQLITE_HASH_H
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct Hash Hash;
|
||||
@@ -93,4 +93,4 @@ void sqlite3HashClear(Hash*);
|
||||
*/
|
||||
/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
|
||||
|
||||
#endif /* _SQLITE_HASH_H_ */
|
||||
#endif /* SQLITE_HASH_H */
|
||||
|
||||
+3
-3
@@ -13,8 +13,8 @@
|
||||
** This file contains inline asm code for retrieving "high-performance"
|
||||
** counters for x86 class CPUs.
|
||||
*/
|
||||
#ifndef _HWTIME_H_
|
||||
#define _HWTIME_H_
|
||||
#ifndef SQLITE_HWTIME_H
|
||||
#define SQLITE_HWTIME_H
|
||||
|
||||
/*
|
||||
** The following routine only works on pentium-class (or newer) processors.
|
||||
@@ -82,4 +82,4 @@
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !defined(_HWTIME_H_) */
|
||||
#endif /* !defined(SQLITE_HWTIME_H) */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
IN-Operator Implementation Notes
|
||||
================================
|
||||
|
||||
## Definitions:
|
||||
|
||||
An IN operator has one of the following formats:
|
||||
|
||||
>
|
||||
x IN (y1,y2,y3,...,yN)
|
||||
x IN (subquery)
|
||||
|
||||
The "x" is referred to as the LHS (left-hand side). The list or subquery
|
||||
on the right is called the RHS (right-hand side). If the RHS is a list
|
||||
it must be a non-empty list. But if the RHS is a subquery, it can be an
|
||||
empty set.
|
||||
|
||||
The LHS can be a scalar (a single quantity) or a vector (a list of
|
||||
two or or more values) or a subquery that returns one or more columns.
|
||||
We use the term "vector" to mean an actually list of values or a
|
||||
subquery that returns two or more columns. An isolated value or
|
||||
a subquery that returns a single columns is called a scalar.
|
||||
|
||||
The RHS can be a subquery that returns a single column, a subquery
|
||||
that returns two or more columns, or a list of scalars. It is not
|
||||
currently support for the RHS to be a list of vectors.
|
||||
|
||||
The number of columns for LHS must match the number of columns for
|
||||
the RHS. If the RHS is a list of values, then the LHS must be a
|
||||
scalar. If the RHS is a subquery returning N columns, then the LHS
|
||||
must be a vector of size N.
|
||||
|
||||
NULL values can occur in either or both of the LHS and RHS.
|
||||
If the LHS contains only
|
||||
NULL values then we say that it is a "total-NULL". If the LHS contains
|
||||
some NULL values and some non-NULL values, then it is a "partial-NULL".
|
||||
For a scalar, there is no difference between a partial-NULL and a total-NULL.
|
||||
The RHS is a partial-NULL if any row contains a NULL value. The RHS is
|
||||
a total-NULL if it contains one or more rows that contain only NULL values.
|
||||
The LHS is called "non-NULL" if it contains no NULL values. The RHS is
|
||||
called "non-NULL" if it contains no NULL values in any row.
|
||||
|
||||
The result of an IN operator is one of TRUE, FALSE, or NULL. A NULL result
|
||||
means that it cannot be determined if the LHS is contained in the RHS due
|
||||
to the presence of NULL values. In some contexts (for example, when the IN
|
||||
operator occurs in a WHERE clause)
|
||||
the system only needs a binary result: TRUE or NOT-TRUE. One can also
|
||||
to define a binary result of FALSE and NOT-FALSE, but
|
||||
it turns out that no extra optimizations are possible in that case, so if
|
||||
the FALSE/NOT-FALSE binary is needed, we have to compute the three-state
|
||||
TRUE/FALSE/NULL result and then combine the TRUE and NULL values into
|
||||
NOT-FALSE.
|
||||
|
||||
A "NOT IN" operator is computed by first computing the equivalent IN
|
||||
operator, then interchanging the TRUE and FALSE results.
|
||||
|
||||
## Simple Full-Scan Algorithm
|
||||
|
||||
The following algorithm always compute the correct answer. However, this
|
||||
algorithm is suboptimal, especially if there are many rows on the RHS.
|
||||
|
||||
1. Set the null-flag to false
|
||||
2. For each row in the RHS:
|
||||
<ol type='a'>
|
||||
<li> Compare the LHS against the RHS
|
||||
<li> If the LHS exactly matches the RHS, immediately return TRUE
|
||||
<li> If the comparison result is NULL, set the null-flag to true
|
||||
</ol>
|
||||
3. If the null-flag is true, return NULL.
|
||||
4. Return FALSE
|
||||
|
||||
## Optimized Algorithm
|
||||
|
||||
The following procedure computes the same answer as the simple full-scan
|
||||
algorithm, though it does so with less work in the common case. This
|
||||
is the algorithm that is implemented in SQLite.
|
||||
|
||||
1. If the RHS is a constant list of length 1 or 2, then rewrite the
|
||||
IN operator as a simple expression. Implement
|
||||
|
||||
x IN (y1,y2)
|
||||
|
||||
as if it were
|
||||
|
||||
x=y1 OR x=y2
|
||||
|
||||
This is the INDEX_NOOP optimization and is only undertaken if the
|
||||
IN operator is used for membership testing. If the IN operator is
|
||||
driving a loop, then skip this step entirely.
|
||||
|
||||
2. Check the LHS to see if it is a partial-NULL and if it is, jump
|
||||
ahead to step 5.
|
||||
|
||||
3. Do a binary search of the RHS using the LHS as a probe. If
|
||||
an exact match is found, return TRUE.
|
||||
|
||||
4. If the RHS is non-NULL then return FALSE.
|
||||
|
||||
5. If we do not need to distinguish between FALSE and NULL,
|
||||
then return FALSE.
|
||||
|
||||
6. For each row in the RHS, compare that row against the LHS and
|
||||
if the result is NULL, immediately return NULL. In the case
|
||||
of a scalar IN operator, we only need to look at the very first
|
||||
row the RHS because for a scalar RHS, all NULLs will always come
|
||||
first. If the RHS is empty, this step is a no-op.
|
||||
|
||||
7. Return FALSE.
|
||||
+36
-21
@@ -200,7 +200,9 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
/*
|
||||
** Locate or create an AutoincInfo structure associated with table pTab
|
||||
** which is in database iDb. Return the register number for the register
|
||||
** that holds the maximum rowid.
|
||||
** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
|
||||
** table. (Also return zero when doing a VACUUM since we do not want to
|
||||
** update the AUTOINCREMENT counters during a VACUUM.)
|
||||
**
|
||||
** There is at most one AutoincInfo structure per table even if the
|
||||
** same table is autoincremented multiple times due to inserts within
|
||||
@@ -223,7 +225,9 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->flags & SQLITE_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
|
||||
@@ -481,7 +485,6 @@ void sqlite3Insert(
|
||||
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 */
|
||||
const char *zDb; /* Name of the database holding this table */
|
||||
int i, j, idx; /* Loop counters */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
@@ -496,7 +499,6 @@ void sqlite3Insert(
|
||||
int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
|
||||
SelectDest dest; /* Destination for SELECT on rhs of INSERT */
|
||||
int iDb; /* Index of database holding TABLE */
|
||||
Db *pDb; /* The database containing table being inserted into */
|
||||
u8 useTempTable = 0; /* Store SELECT results in intermediate table */
|
||||
u8 appendFlag = 0; /* True if the insert is likely to be an append */
|
||||
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
|
||||
@@ -546,9 +548,8 @@ void sqlite3Insert(
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
pDb = &db->aDb[iDb];
|
||||
zDb = pDb->zName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0,
|
||||
db->aDb[iDb].zDbSName) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
withoutRowid = !HasRowid(pTab);
|
||||
@@ -1431,9 +1432,18 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regNewData, 1, 0, OE_Replace,
|
||||
ONEPASS_SINGLE, -1);
|
||||
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);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
if( pTab->pIndex ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1);
|
||||
@@ -1703,7 +1713,7 @@ void sqlite3CompleteInsertion(
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
|
||||
}
|
||||
sqlite3VdbeChangeP5(v, pik_flags);
|
||||
}
|
||||
@@ -1768,15 +1778,15 @@ int sqlite3OpenTableAndIndices(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
int iIdxCur = iBase++;
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
p5 = 0;
|
||||
}
|
||||
if( aToOpen==0 || aToOpen[i+1] ){
|
||||
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
}else{
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
}
|
||||
if( iBase>pParse->nTab ) pParse->nTab = iBase;
|
||||
@@ -1999,11 +2009,15 @@ static int xferOptimization(
|
||||
return 0; /* tab2 must be NOT NULL if tab1 is */
|
||||
}
|
||||
/* Default values for second and subsequent columns need to match. */
|
||||
if( i>0
|
||||
&& ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0)
|
||||
|| (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0))
|
||||
){
|
||||
return 0; /* Default values must be the same for all columns */
|
||||
if( i>0 ){
|
||||
assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
|
||||
assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
|
||||
if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
|
||||
|| (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken,
|
||||
pSrcCol->pDflt->u.zToken)!=0)
|
||||
){
|
||||
return 0; /* Default values must be the same for all columns */
|
||||
}
|
||||
}
|
||||
}
|
||||
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
|
||||
@@ -2099,7 +2113,7 @@ static int xferOptimization(
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
pDest->zName, 0);
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
|
||||
@@ -2163,6 +2177,7 @@ static int xferOptimization(
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
sqlite3ReleaseTempReg(pParse, regData);
|
||||
if( emptyDestTest ){
|
||||
sqlite3AutoincrementEnd(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
|
||||
sqlite3VdbeJumpHere(v, emptyDestTest);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
|
||||
|
||||
-256
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
** 2007 August 22
|
||||
**
|
||||
** 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 a special kind of sqlite3_file object used
|
||||
** by SQLite to create journal files if the atomic-write optimization
|
||||
** is enabled.
|
||||
**
|
||||
** The distinctive characteristic of this sqlite3_file is that the
|
||||
** actual on disk file is created lazily. When the file is created,
|
||||
** the caller specifies a buffer size for an in-memory buffer to
|
||||
** be used to service read() and write() requests. The actual file
|
||||
** on disk is not created or populated until either:
|
||||
**
|
||||
** 1) The in-memory representation grows too large for the allocated
|
||||
** buffer, or
|
||||
** 2) The sqlite3JournalCreate() function is called.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** A JournalFile object is a subclass of sqlite3_file used by
|
||||
** as an open file handle for journal files.
|
||||
*/
|
||||
struct JournalFile {
|
||||
sqlite3_io_methods *pMethod; /* I/O methods on journal files */
|
||||
int nBuf; /* Size of zBuf[] in bytes */
|
||||
char *zBuf; /* Space to buffer journal writes */
|
||||
int iSize; /* Amount of zBuf[] currently used */
|
||||
int flags; /* xOpen flags */
|
||||
sqlite3_vfs *pVfs; /* The "real" underlying VFS */
|
||||
sqlite3_file *pReal; /* The "real" underlying file descriptor */
|
||||
const char *zJournal; /* Name of the journal file */
|
||||
};
|
||||
typedef struct JournalFile JournalFile;
|
||||
|
||||
/*
|
||||
** If it does not already exists, create and populate the on-disk file
|
||||
** for JournalFile p.
|
||||
*/
|
||||
static int createFile(JournalFile *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( !p->pReal ){
|
||||
sqlite3_file *pReal = (sqlite3_file *)&p[1];
|
||||
rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
p->pReal = pReal;
|
||||
if( p->iSize>0 ){
|
||||
assert(p->iSize<=p->nBuf);
|
||||
rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* If an error occurred while writing to the file, close it before
|
||||
** returning. This way, SQLite uses the in-memory journal data to
|
||||
** roll back changes made to the internal page-cache before this
|
||||
** function was called. */
|
||||
sqlite3OsClose(pReal);
|
||||
p->pReal = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
static int jrnlClose(sqlite3_file *pJfd){
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
sqlite3OsClose(p->pReal);
|
||||
}
|
||||
sqlite3_free(p->zBuf);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from the file.
|
||||
*/
|
||||
static int jrnlRead(
|
||||
sqlite3_file *pJfd, /* The journal file from which to read */
|
||||
void *zBuf, /* Put the results here */
|
||||
int iAmt, /* Number of bytes to read */
|
||||
sqlite_int64 iOfst /* Begin reading at this offset */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
}else if( (iAmt+iOfst)>p->iSize ){
|
||||
rc = SQLITE_IOERR_SHORT_READ;
|
||||
}else{
|
||||
memcpy(zBuf, &p->zBuf[iOfst], iAmt);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to the file.
|
||||
*/
|
||||
static int jrnlWrite(
|
||||
sqlite3_file *pJfd, /* The journal file into which to write */
|
||||
const void *zBuf, /* Take data to be written from here */
|
||||
int iAmt, /* Number of bytes to write */
|
||||
sqlite_int64 iOfst /* Begin writing at this offset into the file */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
|
||||
rc = createFile(p);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
|
||||
}else{
|
||||
memcpy(&p->zBuf[iOfst], zBuf, iAmt);
|
||||
if( p->iSize<(iOfst+iAmt) ){
|
||||
p->iSize = (iOfst+iAmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file.
|
||||
*/
|
||||
static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsTruncate(p->pReal, size);
|
||||
}else if( size<p->iSize ){
|
||||
p->iSize = size;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the file.
|
||||
*/
|
||||
static int jrnlSync(sqlite3_file *pJfd, int flags){
|
||||
int rc;
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsSync(p->pReal, flags);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the size of the file in bytes.
|
||||
*/
|
||||
static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
|
||||
int rc = SQLITE_OK;
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsFileSize(p->pReal, pSize);
|
||||
}else{
|
||||
*pSize = (sqlite_int64) p->iSize;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table of methods for JournalFile sqlite3_file object.
|
||||
*/
|
||||
static struct sqlite3_io_methods JournalFileMethods = {
|
||||
1, /* iVersion */
|
||||
jrnlClose, /* xClose */
|
||||
jrnlRead, /* xRead */
|
||||
jrnlWrite, /* xWrite */
|
||||
jrnlTruncate, /* xTruncate */
|
||||
jrnlSync, /* xSync */
|
||||
jrnlFileSize, /* xFileSize */
|
||||
0, /* xLock */
|
||||
0, /* xUnlock */
|
||||
0, /* xCheckReservedLock */
|
||||
0, /* xFileControl */
|
||||
0, /* xSectorSize */
|
||||
0, /* xDeviceCharacteristics */
|
||||
0, /* xShmMap */
|
||||
0, /* xShmLock */
|
||||
0, /* xShmBarrier */
|
||||
0 /* xShmUnmap */
|
||||
};
|
||||
|
||||
/*
|
||||
** Open a journal file.
|
||||
*/
|
||||
int sqlite3JournalOpen(
|
||||
sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
|
||||
const char *zName, /* Name of the journal file */
|
||||
sqlite3_file *pJfd, /* Preallocated, blank file handle */
|
||||
int flags, /* Opening flags */
|
||||
int nBuf /* Bytes buffered before opening the file */
|
||||
){
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
memset(p, 0, sqlite3JournalSize(pVfs));
|
||||
if( nBuf>0 ){
|
||||
p->zBuf = sqlite3MallocZero(nBuf);
|
||||
if( !p->zBuf ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}else{
|
||||
return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
|
||||
}
|
||||
p->pMethod = &JournalFileMethods;
|
||||
p->nBuf = nBuf;
|
||||
p->flags = flags;
|
||||
p->zJournal = zName;
|
||||
p->pVfs = pVfs;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the argument p points to a JournalFile structure, and the underlying
|
||||
** file has not yet been created, create it now.
|
||||
*/
|
||||
int sqlite3JournalCreate(sqlite3_file *p){
|
||||
if( p->pMethods!=&JournalFileMethods ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return createFile((JournalFile *)p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The file-handle passed as the only argument is guaranteed to be an open
|
||||
** file. It may or may not be of class JournalFile. If the file is a
|
||||
** JournalFile, and the underlying file on disk has not yet been opened,
|
||||
** return 0. Otherwise, return 1.
|
||||
*/
|
||||
int sqlite3JournalExists(sqlite3_file *p){
|
||||
return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a JournalFile that uses vfs
|
||||
** pVfs to create the underlying on-disk files.
|
||||
*/
|
||||
int sqlite3JournalSize(sqlite3_vfs *pVfs){
|
||||
return (pVfs->szOsFile+sizeof(JournalFile));
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -131,7 +131,7 @@ exec_out:
|
||||
if( *pzErrMsg ){
|
||||
memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
sqlite3Error(db, SQLITE_NOMEM);
|
||||
}
|
||||
}else if( pzErrMsg ){
|
||||
|
||||
+42
-35
@@ -21,7 +21,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
/*
|
||||
** Some API routines are omitted when various features are
|
||||
** excluded from a build of SQLite. Substitute a NULL pointer
|
||||
@@ -91,7 +90,7 @@
|
||||
# define sqlite3_enable_shared_cache 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
#if defined(SQLITE_OMIT_TRACE) || defined(SQLITE_OMIT_DEPRECATED)
|
||||
# define sqlite3_profile 0
|
||||
# define sqlite3_trace 0
|
||||
#endif
|
||||
@@ -111,6 +110,10 @@
|
||||
#define sqlite3_blob_reopen 0
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_OMIT_TRACE)
|
||||
# define sqlite3_trace_v2 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following structure contains pointers to all SQLite API routines.
|
||||
** A pointer to this structure is passed into extensions when they are
|
||||
@@ -414,7 +417,12 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
/* Version 3.10.0 and later */
|
||||
sqlite3_status64,
|
||||
sqlite3_strlike,
|
||||
sqlite3_db_cacheflush
|
||||
sqlite3_db_cacheflush,
|
||||
/* Version 3.12.0 and later */
|
||||
sqlite3_system_errno,
|
||||
/* Version 3.14.0 and later */
|
||||
sqlite3_trace_v2,
|
||||
sqlite3_expanded_sql
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -437,13 +445,14 @@ static int sqlite3LoadExtension(
|
||||
){
|
||||
sqlite3_vfs *pVfs = db->pVfs;
|
||||
void *handle;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
sqlite3_loadext_entry xInit;
|
||||
char *zErrmsg = 0;
|
||||
const char *zEntry;
|
||||
char *zAltEntry = 0;
|
||||
void **aHandle;
|
||||
u64 nMsg = 300 + sqlite3Strlen30(zFile);
|
||||
int ii;
|
||||
int rc;
|
||||
|
||||
/* Shared library endings to try if zFile cannot be loaded as written */
|
||||
static const char *azEndings[] = {
|
||||
@@ -462,8 +471,9 @@ static int sqlite3LoadExtension(
|
||||
/* Ticket #1863. To avoid a creating security problems for older
|
||||
** applications that relink against newer versions of SQLite, the
|
||||
** ability to run load_extension is turned off by default. One
|
||||
** must call sqlite3_enable_load_extension() to turn on extension
|
||||
** loading. Otherwise you get the following error.
|
||||
** must call either sqlite3_enable_load_extension(db) or
|
||||
** sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 0)
|
||||
** to turn on extension loading.
|
||||
*/
|
||||
if( (db->flags & SQLITE_LoadExtension)==0 ){
|
||||
if( pzErrMsg ){
|
||||
@@ -478,7 +488,7 @@ static int sqlite3LoadExtension(
|
||||
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
|
||||
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
|
||||
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
|
||||
if( zAltFile==0 ) return SQLITE_NOMEM;
|
||||
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
|
||||
handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||||
sqlite3_free(zAltFile);
|
||||
}
|
||||
@@ -494,8 +504,7 @@ static int sqlite3LoadExtension(
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
|
||||
/* If no entry point was specified and the default legacy
|
||||
** entry point name "sqlite3_extension_init" was not found, then
|
||||
@@ -514,7 +523,7 @@ static int sqlite3LoadExtension(
|
||||
zAltEntry = sqlite3_malloc64(ncFile+30);
|
||||
if( zAltEntry==0 ){
|
||||
sqlite3OsDlClose(pVfs, handle);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memcpy(zAltEntry, "sqlite3_", 8);
|
||||
for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
|
||||
@@ -527,8 +536,7 @@ static int sqlite3LoadExtension(
|
||||
}
|
||||
memcpy(zAltEntry+iEntry, "_init", 6);
|
||||
zEntry = zAltEntry;
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
}
|
||||
if( xInit==0 ){
|
||||
if( pzErrMsg ){
|
||||
@@ -545,7 +553,9 @@ static int sqlite3LoadExtension(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqlite3_free(zAltEntry);
|
||||
if( xInit(db, &zErrmsg, &sqlite3Apis) ){
|
||||
rc = xInit(db, &zErrmsg, &sqlite3Apis);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_OK_LOAD_PERMANENTLY ) return SQLITE_OK;
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
|
||||
}
|
||||
@@ -557,7 +567,7 @@ static int sqlite3LoadExtension(
|
||||
/* Append the new shared library handle to the db->aExtension array. */
|
||||
aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1));
|
||||
if( aHandle==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( db->nExtension>0 ){
|
||||
memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension);
|
||||
@@ -602,26 +612,15 @@ void sqlite3CloseExtensions(sqlite3 *db){
|
||||
int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( onoff ){
|
||||
db->flags |= SQLITE_LoadExtension;
|
||||
db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_LoadExtension;
|
||||
db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc);
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** The auto-extension code added regardless of whether or not extension
|
||||
** loading is supported. We need a dummy sqlite3Apis pointer for that
|
||||
** code if regular extension loading is not available. This is that
|
||||
** dummy pointer.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
static const sqlite3_api_routines sqlite3Apis = { 0 };
|
||||
#endif
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
/*
|
||||
** The following object holds the list of automatically loaded
|
||||
@@ -656,7 +655,9 @@ static SQLITE_WSD struct sqlite3AutoExtList {
|
||||
** Register a statically linked extension that is automatically
|
||||
** loaded by every new database connection.
|
||||
*/
|
||||
int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
int sqlite3_auto_extension(
|
||||
void (*xInit)(void)
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
@@ -679,7 +680,7 @@ int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
void (**aNew)(void);
|
||||
aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte);
|
||||
if( aNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
wsdAutoext.aExt = aNew;
|
||||
wsdAutoext.aExt[wsdAutoext.nExt] = xInit;
|
||||
@@ -701,7 +702,9 @@ int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
** Return 1 if xInit was found on the list and removed. Return 0 if xInit
|
||||
** was not on the list.
|
||||
*/
|
||||
int sqlite3_cancel_auto_extension(void (*xInit)(void)){
|
||||
int sqlite3_cancel_auto_extension(
|
||||
void (*xInit)(void)
|
||||
){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
@@ -750,7 +753,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
u32 i;
|
||||
int go = 1;
|
||||
int rc;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
sqlite3_loadext_entry xInit;
|
||||
|
||||
wsdAutoextInit;
|
||||
if( wsdAutoext.nExt==0 ){
|
||||
@@ -761,18 +764,22 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
char *zErrmsg;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
const sqlite3_api_routines *pThunk = 0;
|
||||
#else
|
||||
const sqlite3_api_routines *pThunk = &sqlite3Apis;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
if( i>=wsdAutoext.nExt ){
|
||||
xInit = 0;
|
||||
go = 0;
|
||||
}else{
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
wsdAutoext.aExt[i];
|
||||
xInit = (sqlite3_loadext_entry)wsdAutoext.aExt[i];
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
zErrmsg = 0;
|
||||
if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
|
||||
if( xInit && (rc = xInit(db, &zErrmsg, pThunk))!=0 ){
|
||||
sqlite3ErrorWithMsg(db, rc,
|
||||
"automatic extension loading failed: %s", zErrmsg);
|
||||
go = 0;
|
||||
|
||||
+152
-67
@@ -187,7 +187,7 @@ int sqlite3_initialize(void){
|
||||
sqlite3GlobalConfig.pInitMutex =
|
||||
sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +218,6 @@ int sqlite3_initialize(void){
|
||||
*/
|
||||
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
|
||||
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
sqlite3GlobalConfig.inProgress = 1;
|
||||
#ifdef SQLITE_ENABLE_SQLLOG
|
||||
{
|
||||
@@ -226,8 +225,8 @@ int sqlite3_initialize(void){
|
||||
sqlite3_init_sqllog();
|
||||
}
|
||||
#endif
|
||||
memset(pHash, 0, sizeof(sqlite3GlobalFunctions));
|
||||
sqlite3RegisterGlobalFunctions();
|
||||
memset(&sqlite3BuiltinFunctions, 0, sizeof(sqlite3BuiltinFunctions));
|
||||
sqlite3RegisterBuiltinFunctions();
|
||||
if( sqlite3GlobalConfig.isPCacheInit==0 ){
|
||||
rc = sqlite3PcacheInitialize();
|
||||
}
|
||||
@@ -634,6 +633,11 @@ int sqlite3_config(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_STMTJRNL_SPILL: {
|
||||
sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@@ -785,6 +789,11 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
int rc;
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_DBCONFIG_MAINDBNAME: {
|
||||
db->aDb[0].zDbSName = va_arg(ap,char*);
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
}
|
||||
case SQLITE_DBCONFIG_LOOKASIDE: {
|
||||
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
|
||||
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
|
||||
@@ -797,8 +806,10 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
int op; /* The opcode */
|
||||
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
|
||||
} aFlagOp[] = {
|
||||
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
|
||||
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
|
||||
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
|
||||
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
|
||||
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
|
||||
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
|
||||
};
|
||||
unsigned int i;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
@@ -958,7 +969,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
|
||||
** with SQLITE_ANY as the encoding.
|
||||
*/
|
||||
static void functionDestroy(sqlite3 *db, FuncDef *p){
|
||||
FuncDestructor *pDestructor = p->pDestructor;
|
||||
FuncDestructor *pDestructor = p->u.pDestructor;
|
||||
if( pDestructor ){
|
||||
pDestructor->nRef--;
|
||||
if( pDestructor->nRef==0 ){
|
||||
@@ -1027,6 +1038,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mTrace & SQLITE_TRACE_CLOSE ){
|
||||
db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
}
|
||||
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
disconnectAllVtab(db);
|
||||
@@ -1140,18 +1154,17 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
*/
|
||||
sqlite3ConnectionClosed(db);
|
||||
|
||||
for(j=0; j<ArraySize(db->aFunc.a); j++){
|
||||
FuncDef *pNext, *pHash, *p;
|
||||
for(p=db->aFunc.a[j]; p; p=pHash){
|
||||
pHash = p->pHash;
|
||||
while( p ){
|
||||
functionDestroy(db, p);
|
||||
pNext = p->pNext;
|
||||
sqlite3DbFree(db, p);
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
|
||||
FuncDef *pNext, *p;
|
||||
p = sqliteHashData(i);
|
||||
do{
|
||||
functionDestroy(db, p);
|
||||
pNext = p->pNext;
|
||||
sqlite3DbFree(db, p);
|
||||
p = pNext;
|
||||
}while( p );
|
||||
}
|
||||
sqlite3HashClear(&db->aFunc);
|
||||
for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
|
||||
CollSeq *pColl = (CollSeq *)sqliteHashData(i);
|
||||
/* Invoke any destructors registered for collation sequence user data. */
|
||||
@@ -1630,7 +1643,7 @@ int sqlite3CreateFunc(
|
||||
** is being overridden/deleted but there are no active VMs, allow the
|
||||
** operation to continue but invalidate all precompiled statements.
|
||||
*/
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
|
||||
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
|
||||
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
|
||||
if( db->nVdbeActive ){
|
||||
sqlite3ErrorWithMsg(db, SQLITE_BUSY,
|
||||
@@ -1642,10 +1655,10 @@ int sqlite3CreateFunc(
|
||||
}
|
||||
}
|
||||
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1);
|
||||
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 1);
|
||||
assert(p || db->mallocFailed);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
/* If an older version of the function with a configured destructor is
|
||||
@@ -1655,7 +1668,7 @@ int sqlite3CreateFunc(
|
||||
if( pDestructor ){
|
||||
pDestructor->nRef++;
|
||||
}
|
||||
p->pDestructor = pDestructor;
|
||||
p->u.pDestructor = pDestructor;
|
||||
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
|
||||
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
||||
p->xSFunc = xSFunc ? xSFunc : xStep;
|
||||
@@ -1770,7 +1783,6 @@ int sqlite3_overload_function(
|
||||
const char *zName,
|
||||
int nArg
|
||||
){
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
@@ -1779,7 +1791,7 @@ int sqlite3_overload_function(
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
0, sqlite3InvalidFunction, 0, 0, 0);
|
||||
}
|
||||
@@ -1797,7 +1809,8 @@ int sqlite3_overload_function(
|
||||
** trace is a pointer to a function that is invoked at the start of each
|
||||
** SQL statement.
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
@@ -1808,11 +1821,38 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->xTrace = xTrace;
|
||||
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
|
||||
db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
|
||||
/* Register a trace callback using the version-2 interface.
|
||||
*/
|
||||
int sqlite3_trace_v2(
|
||||
sqlite3 *db, /* Trace this connection */
|
||||
unsigned mTrace, /* Mask of events to be traced */
|
||||
int(*xTrace)(unsigned,void*,void*,void*), /* Callback to invoke */
|
||||
void *pArg /* Context */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( mTrace==0 ) xTrace = 0;
|
||||
if( xTrace==0 ) mTrace = 0;
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Register a profile function. The pArg from the previously registered
|
||||
** profile function is returned.
|
||||
@@ -1841,6 +1881,7 @@ void *sqlite3_profile(
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
|
||||
/*
|
||||
@@ -1919,6 +1960,27 @@ void *sqlite3_rollback_hook(
|
||||
return pRet;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
/*
|
||||
** Register a callback to be invoked each time a row is updated,
|
||||
** inserted or deleted using this database connection.
|
||||
*/
|
||||
void *sqlite3_preupdate_hook(
|
||||
sqlite3 *db, /* Attach the hook to this database */
|
||||
void(*xCallback)( /* Callback function */
|
||||
void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64),
|
||||
void *pArg /* First callback argument */
|
||||
){
|
||||
void *pRet;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pRet = db->pPreUpdateArg;
|
||||
db->xPreUpdateCallback = xCallback;
|
||||
db->pPreUpdateArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pRet;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
|
||||
@@ -2149,14 +2211,14 @@ int sqlite3TempInMemory(const sqlite3 *db){
|
||||
const char *sqlite3_errmsg(sqlite3 *db){
|
||||
const char *z;
|
||||
if( !db ){
|
||||
return sqlite3ErrStr(SQLITE_NOMEM);
|
||||
return sqlite3ErrStr(SQLITE_NOMEM_BKPT);
|
||||
}
|
||||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return sqlite3ErrStr(SQLITE_MISUSE_BKPT);
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mallocFailed ){
|
||||
z = sqlite3ErrStr(SQLITE_NOMEM);
|
||||
z = sqlite3ErrStr(SQLITE_NOMEM_BKPT);
|
||||
}else{
|
||||
testcase( db->pErr==0 );
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
@@ -2224,7 +2286,7 @@ int sqlite3_errcode(sqlite3 *db){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
if( !db || db->mallocFailed ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
return db->errCode & db->errMask;
|
||||
}
|
||||
@@ -2233,10 +2295,13 @@ int sqlite3_extended_errcode(sqlite3 *db){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
if( !db || db->mallocFailed ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
return db->errCode;
|
||||
}
|
||||
int sqlite3_system_errno(sqlite3 *db){
|
||||
return db ? db->iSysErrno : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a string that describes the kind of error specified in the
|
||||
@@ -2313,7 +2378,7 @@ static int createCollation(
|
||||
}
|
||||
|
||||
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1);
|
||||
if( pColl==0 ) return SQLITE_NOMEM;
|
||||
if( pColl==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pColl->xCmp = xCompare;
|
||||
pColl->pUser = pCtx;
|
||||
pColl->xDel = xDel;
|
||||
@@ -2361,8 +2426,8 @@ static const int aHardLimit[] = {
|
||||
#if SQLITE_MAX_VDBE_OP<40
|
||||
# error SQLITE_MAX_VDBE_OP must be at least 40
|
||||
#endif
|
||||
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
|
||||
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
|
||||
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
|
||||
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
|
||||
#endif
|
||||
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
|
||||
# error SQLITE_MAX_ATTACHED must be between 0 and 125
|
||||
@@ -2492,7 +2557,7 @@ int sqlite3ParseUri(
|
||||
|
||||
for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
|
||||
zFile = sqlite3_malloc64(nByte);
|
||||
if( !zFile ) return SQLITE_NOMEM;
|
||||
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
||||
|
||||
iIn = 5;
|
||||
#ifdef SQLITE_ALLOW_URI_AUTHORITY
|
||||
@@ -2658,7 +2723,7 @@ int sqlite3ParseUri(
|
||||
|
||||
}else{
|
||||
zFile = sqlite3_malloc64(nUri+2);
|
||||
if( !zFile ) return SQLITE_NOMEM;
|
||||
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
||||
memcpy(zFile, zUri, nUri);
|
||||
zFile[nUri] = '\0';
|
||||
zFile[nUri+1] = '\0';
|
||||
@@ -2814,6 +2879,9 @@ static int openDatabase(
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
|
||||
| SQLITE_CellSizeCk
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_FTS3_TOKENIZER)
|
||||
| SQLITE_Fts3Tokenizer
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
@@ -2857,7 +2925,7 @@ static int openDatabase(
|
||||
flags | SQLITE_OPEN_MAIN_DB);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_IOERR_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
sqlite3Error(db, rc);
|
||||
goto opendb_out;
|
||||
@@ -2868,13 +2936,13 @@ static int openDatabase(
|
||||
sqlite3BtreeLeave(db->aDb[0].pBt);
|
||||
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
|
||||
|
||||
/* The default safety_level for the main database is 'full'; for the temp
|
||||
** database it is 'NONE'. This matches the pager layer defaults.
|
||||
/* The default safety_level for the main database is FULL; for the temp
|
||||
** database it is OFF. This matches the pager layer defaults.
|
||||
*/
|
||||
db->aDb[0].zName = "main";
|
||||
db->aDb[0].safety_level = 3;
|
||||
db->aDb[1].zName = "temp";
|
||||
db->aDb[1].safety_level = 1;
|
||||
db->aDb[0].zDbSName = "main";
|
||||
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
db->aDb[1].zDbSName = "temp";
|
||||
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
|
||||
|
||||
db->magic = SQLITE_MAGIC_OPEN;
|
||||
if( db->mallocFailed ){
|
||||
@@ -2886,12 +2954,21 @@ static int openDatabase(
|
||||
** is accessed.
|
||||
*/
|
||||
sqlite3Error(db, SQLITE_OK);
|
||||
sqlite3RegisterBuiltinFunctions(db);
|
||||
sqlite3RegisterPerConnectionBuiltinFunctions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
/* Register any built-in FTS5 module before loading the automatic
|
||||
** extensions. This allows automatic extensions to register FTS5
|
||||
** tokenizers and auxiliary functions. */
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
*/
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
@@ -2920,12 +2997,6 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3IcuInit(db);
|
||||
@@ -3060,7 +3131,7 @@ int sqlite3_open16(
|
||||
SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
|
||||
@@ -3205,7 +3276,7 @@ int sqlite3_get_autocommit(sqlite3 *db){
|
||||
|
||||
/*
|
||||
** The following routines are substitutes for constants SQLITE_CORRUPT,
|
||||
** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error
|
||||
** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_NOMEM and possibly other error
|
||||
** constants. They serve two purposes:
|
||||
**
|
||||
** 1. Serve as a convenient place to set a breakpoint in a debugger
|
||||
@@ -3214,28 +3285,33 @@ 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){
|
||||
sqlite3_log(iErr, "%s at line %d of [%.10s]",
|
||||
zType, lineno, 20+sqlite3_sourceid());
|
||||
return iErr;
|
||||
}
|
||||
int sqlite3CorruptError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(SQLITE_CORRUPT,
|
||||
"database corruption at line %d of [%.10s]",
|
||||
lineno, 20+sqlite3_sourceid());
|
||||
return SQLITE_CORRUPT;
|
||||
return reportError(SQLITE_CORRUPT, lineno, "database corruption");
|
||||
}
|
||||
int sqlite3MisuseError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(SQLITE_MISUSE,
|
||||
"misuse at line %d of [%.10s]",
|
||||
lineno, 20+sqlite3_sourceid());
|
||||
return SQLITE_MISUSE;
|
||||
return reportError(SQLITE_MISUSE, lineno, "misuse");
|
||||
}
|
||||
int sqlite3CantopenError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(SQLITE_CANTOPEN,
|
||||
"cannot open file at line %d of [%.10s]",
|
||||
lineno, 20+sqlite3_sourceid());
|
||||
return SQLITE_CANTOPEN;
|
||||
return reportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3NomemError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_NOMEM, lineno, "OOM");
|
||||
}
|
||||
int sqlite3IoerrnomemError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
@@ -3329,7 +3405,7 @@ int sqlite3_table_column_metadata(
|
||||
** explicitly declared column. Copy meta information from *pCol.
|
||||
*/
|
||||
if( pCol ){
|
||||
zDataType = pCol->zType;
|
||||
zDataType = sqlite3ColumnType(pCol,0);
|
||||
zCollSeq = pCol->zColl;
|
||||
notnull = pCol->notNull!=0;
|
||||
primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0;
|
||||
@@ -3708,6 +3784,15 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the threshold at which OP_Once counters reset back to zero.
|
||||
** By default this is 0x7ffffffe (over 2 billion), but that value is
|
||||
** too big to test in a reasonable amount of time, so this control is
|
||||
** provided to set a small and easily reachable reset value.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: {
|
||||
sqlite3GlobalConfig.iOnceResetThreshold = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
|
||||
**
|
||||
@@ -3829,7 +3914,7 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zDbSName)==0)
|
||||
){
|
||||
return db->aDb[i].pBt;
|
||||
}
|
||||
|
||||
+1
-1
@@ -795,7 +795,7 @@ void sqlite3OomClear(sqlite3 *db){
|
||||
static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
|
||||
sqlite3OomClear(db);
|
||||
sqlite3Error(db, SQLITE_NOMEM);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+245
-82
@@ -13,6 +13,15 @@
|
||||
** This file contains code use to implement an in-memory rollback journal.
|
||||
** The in-memory rollback journal is used to journal transactions for
|
||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||
**
|
||||
** Update: The in-memory journal is also used to temporarily cache
|
||||
** smaller journals that are not critical for power-loss recovery.
|
||||
** For example, statement journals that are not too big will be held
|
||||
** entirely in memory, thus reducing the number of file I/O calls, and
|
||||
** more importantly, reducing temporary file creation events. If these
|
||||
** journals become too large for memory, they are spilled to disk. But
|
||||
** in the common case, they are usually small and no file I/O needs to
|
||||
** occur.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -21,24 +30,28 @@ typedef struct MemJournal MemJournal;
|
||||
typedef struct FilePoint FilePoint;
|
||||
typedef struct FileChunk FileChunk;
|
||||
|
||||
/* Space to hold the rollback journal is allocated in increments of
|
||||
** this many bytes.
|
||||
**
|
||||
** The size chosen is a little less than a power of two. That way,
|
||||
** the FileChunk object will have a size that almost exactly fills
|
||||
** a power-of-two allocation. This minimizes wasted space in power-of-two
|
||||
** memory allocators.
|
||||
*/
|
||||
#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
|
||||
|
||||
/*
|
||||
** The rollback journal is composed of a linked list of these structures.
|
||||
**
|
||||
** The zChunk array is always at least 8 bytes in size - usually much more.
|
||||
** Its actual size is stored in the MemJournal.nChunkSize variable.
|
||||
*/
|
||||
struct FileChunk {
|
||||
FileChunk *pNext; /* Next chunk in the journal */
|
||||
u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
|
||||
u8 zChunk[8]; /* Content of this chunk */
|
||||
};
|
||||
|
||||
/*
|
||||
** By default, allocate this many bytes of memory for each FileChunk object.
|
||||
*/
|
||||
#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
|
||||
|
||||
/*
|
||||
** For chunk size nChunkSize, return the number of bytes that should
|
||||
** be allocated for each FileChunk structure.
|
||||
*/
|
||||
#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
|
||||
|
||||
/*
|
||||
** An instance of this object serves as a cursor into the rollback journal.
|
||||
** The cursor can be either for reading or writing.
|
||||
@@ -49,14 +62,22 @@ struct FilePoint {
|
||||
};
|
||||
|
||||
/*
|
||||
** This subclass is a subclass of sqlite3_file. Each open memory-journal
|
||||
** This structure is a subclass of sqlite3_file. Each open memory-journal
|
||||
** is an instance of this class.
|
||||
*/
|
||||
struct MemJournal {
|
||||
sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
|
||||
const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
|
||||
int nChunkSize; /* In-memory chunk-size */
|
||||
|
||||
int nSpill; /* Bytes of data before flushing */
|
||||
int nSize; /* Bytes of data currently in memory */
|
||||
FileChunk *pFirst; /* Head of in-memory chunk-list */
|
||||
FilePoint endpoint; /* Pointer to the end of the file */
|
||||
FilePoint readpoint; /* Pointer to the end of the last xRead() */
|
||||
|
||||
int flags; /* xOpen flags */
|
||||
sqlite3_vfs *pVfs; /* The "real" underlying VFS */
|
||||
const char *zJournal; /* Name of the journal file */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -75,36 +96,94 @@ static int memjrnlRead(
|
||||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
/* SQLite never tries to read past the end of a rollback journal file */
|
||||
assert( iOfst+iAmt<=p->endpoint.iOffset );
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
if( (iAmt+iOfst)>p->endpoint.iOffset ){
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert( (iAmt+iOfst)<=p->endpoint.iOffset );
|
||||
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
|
||||
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
|
||||
sqlite3_int64 iOff = 0;
|
||||
for(pChunk=p->pFirst;
|
||||
ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
|
||||
ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
|
||||
pChunk=pChunk->pNext
|
||||
){
|
||||
iOff += JOURNAL_CHUNKSIZE;
|
||||
iOff += p->nChunkSize;
|
||||
}
|
||||
}else{
|
||||
pChunk = p->readpoint.pChunk;
|
||||
assert( pChunk!=0 );
|
||||
}
|
||||
|
||||
iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
|
||||
iChunkOffset = (int)(iOfst%p->nChunkSize);
|
||||
do {
|
||||
int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
|
||||
int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
|
||||
memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
|
||||
int iSpace = p->nChunkSize - iChunkOffset;
|
||||
int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
|
||||
memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);
|
||||
zOut += nCopy;
|
||||
nRead -= iSpace;
|
||||
iChunkOffset = 0;
|
||||
} while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
|
||||
p->readpoint.iOffset = iOfst+iAmt;
|
||||
p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0;
|
||||
p->readpoint.pChunk = pChunk;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the list of FileChunk structures headed at MemJournal.pFirst.
|
||||
*/
|
||||
static void memjrnlFreeChunks(MemJournal *p){
|
||||
FileChunk *pIter;
|
||||
FileChunk *pNext;
|
||||
for(pIter=p->pFirst; pIter; pIter=pNext){
|
||||
pNext = pIter->pNext;
|
||||
sqlite3_free(pIter);
|
||||
}
|
||||
p->pFirst = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Flush the contents of memory to a real file on disk.
|
||||
*/
|
||||
static int memjrnlCreateFile(MemJournal *p){
|
||||
int rc;
|
||||
sqlite3_file *pReal = (sqlite3_file*)p;
|
||||
MemJournal copy = *p;
|
||||
|
||||
memset(p, 0, sizeof(MemJournal));
|
||||
rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nChunk = copy.nChunkSize;
|
||||
i64 iOff = 0;
|
||||
FileChunk *pIter;
|
||||
for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){
|
||||
if( iOff + nChunk > copy.endpoint.iOffset ){
|
||||
nChunk = copy.endpoint.iOffset - iOff;
|
||||
}
|
||||
rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff);
|
||||
if( rc ) break;
|
||||
iOff += nChunk;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* No error has occurred. Free the in-memory buffers. */
|
||||
memjrnlFreeChunks(©);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* If an error occurred while creating or writing to the file, restore
|
||||
** the original before returning. This way, SQLite uses the in-memory
|
||||
** journal data to roll back changes made to the internal page-cache
|
||||
** before this function was called. */
|
||||
sqlite3OsClose(pReal);
|
||||
*p = copy;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Write data to the file.
|
||||
*/
|
||||
@@ -118,38 +197,62 @@ static int memjrnlWrite(
|
||||
int nWrite = iAmt;
|
||||
u8 *zWrite = (u8 *)zBuf;
|
||||
|
||||
/* An in-memory journal file should only ever be appended to. Random
|
||||
** access writes are not required by sqlite.
|
||||
*/
|
||||
assert( iOfst==p->endpoint.iOffset );
|
||||
UNUSED_PARAMETER(iOfst);
|
||||
|
||||
while( nWrite>0 ){
|
||||
FileChunk *pChunk = p->endpoint.pChunk;
|
||||
int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
|
||||
int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
|
||||
|
||||
if( iChunkOffset==0 ){
|
||||
/* New chunk is required to extend the file. */
|
||||
FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
|
||||
if( !pNew ){
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
pNew->pNext = 0;
|
||||
if( pChunk ){
|
||||
assert( p->pFirst );
|
||||
pChunk->pNext = pNew;
|
||||
}else{
|
||||
assert( !p->pFirst );
|
||||
p->pFirst = pNew;
|
||||
}
|
||||
p->endpoint.pChunk = pNew;
|
||||
/* If the file should be created now, create it and write the new data
|
||||
** into the file on disk. */
|
||||
if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){
|
||||
int rc = memjrnlCreateFile(p);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
|
||||
zWrite += iSpace;
|
||||
nWrite -= iSpace;
|
||||
p->endpoint.iOffset += iSpace;
|
||||
/* If the contents of this write should be stored in memory */
|
||||
else{
|
||||
/* An in-memory journal file should only ever be appended to. Random
|
||||
** access writes are not required. The only exception to this is when
|
||||
** the in-memory journal is being used by a connection using the
|
||||
** 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( iOfst==0 && p->pFirst ){
|
||||
assert( p->nChunkSize>iAmt );
|
||||
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
|
||||
}else
|
||||
#else
|
||||
assert( iOfst>0 || p->pFirst==0 );
|
||||
#endif
|
||||
{
|
||||
while( nWrite>0 ){
|
||||
FileChunk *pChunk = p->endpoint.pChunk;
|
||||
int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
|
||||
int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
|
||||
|
||||
if( iChunkOffset==0 ){
|
||||
/* New chunk is required to extend the file. */
|
||||
FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
|
||||
if( !pNew ){
|
||||
return SQLITE_IOERR_NOMEM_BKPT;
|
||||
}
|
||||
pNew->pNext = 0;
|
||||
if( pChunk ){
|
||||
assert( p->pFirst );
|
||||
pChunk->pNext = pNew;
|
||||
}else{
|
||||
assert( !p->pFirst );
|
||||
p->pFirst = pNew;
|
||||
}
|
||||
p->endpoint.pChunk = pNew;
|
||||
}
|
||||
|
||||
memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
|
||||
zWrite += iSpace;
|
||||
nWrite -= iSpace;
|
||||
p->endpoint.iOffset += iSpace;
|
||||
}
|
||||
p->nSize = iAmt + iOfst;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
@@ -157,19 +260,21 @@ static int memjrnlWrite(
|
||||
|
||||
/*
|
||||
** Truncate the file.
|
||||
**
|
||||
** If the journal file is already on disk, truncate it there. Or, if it
|
||||
** is still in main memory but is being truncated to zero bytes in size,
|
||||
** ignore
|
||||
*/
|
||||
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
FileChunk *pChunk;
|
||||
assert(size==0);
|
||||
UNUSED_PARAMETER(size);
|
||||
pChunk = p->pFirst;
|
||||
while( pChunk ){
|
||||
FileChunk *pTmp = pChunk;
|
||||
pChunk = pChunk->pNext;
|
||||
sqlite3_free(pTmp);
|
||||
if( ALWAYS(size==0) ){
|
||||
memjrnlFreeChunks(p);
|
||||
p->nSize = 0;
|
||||
p->endpoint.pChunk = 0;
|
||||
p->endpoint.iOffset = 0;
|
||||
p->readpoint.pChunk = 0;
|
||||
p->readpoint.iOffset = 0;
|
||||
}
|
||||
sqlite3MemJournalOpen(pJfd);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -177,21 +282,19 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
|
||||
** Close the file.
|
||||
*/
|
||||
static int memjrnlClose(sqlite3_file *pJfd){
|
||||
memjrnlTruncate(pJfd, 0);
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
memjrnlFreeChunks(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Sync the file.
|
||||
**
|
||||
** Syncing an in-memory journal is a no-op. And, in fact, this routine
|
||||
** is never called in a working implementation. This implementation
|
||||
** exists purely as a contingency, in case some malfunction in some other
|
||||
** part of SQLite causes Sync to be called by mistake.
|
||||
** If the real file has been created, call its xSync method. Otherwise,
|
||||
** syncing an in-memory journal is a no-op.
|
||||
*/
|
||||
static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
static int memjrnlSync(sqlite3_file *pJfd, int flags){
|
||||
UNUSED_PARAMETER2(pJfd, flags);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -230,26 +333,86 @@ static const struct sqlite3_io_methods MemJournalMethods = {
|
||||
};
|
||||
|
||||
/*
|
||||
** Open a journal file.
|
||||
** Open a journal file.
|
||||
**
|
||||
** The behaviour of the journal file depends on the value of parameter
|
||||
** nSpill. If nSpill is 0, then the journal file is always create and
|
||||
** accessed using the underlying VFS. If nSpill is less than zero, then
|
||||
** all content is always stored in main-memory. Finally, if nSpill is a
|
||||
** positive value, then the journal file is initially created in-memory
|
||||
** but may be flushed to disk later on. In this case the journal file is
|
||||
** flushed to disk either when it grows larger than nSpill bytes in size,
|
||||
** or when sqlite3JournalCreate() is called.
|
||||
*/
|
||||
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(p) );
|
||||
memset(p, 0, sqlite3MemJournalSize());
|
||||
p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
|
||||
int sqlite3JournalOpen(
|
||||
sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
|
||||
const char *zName, /* Name of the journal file */
|
||||
sqlite3_file *pJfd, /* Preallocated, blank file handle */
|
||||
int flags, /* Opening flags */
|
||||
int nSpill /* Bytes buffered before opening the file */
|
||||
){
|
||||
MemJournal *p = (MemJournal*)pJfd;
|
||||
|
||||
/* Zero the file-handle object. If nSpill was passed zero, initialize
|
||||
** it using the sqlite3OsOpen() function of the underlying VFS. In this
|
||||
** case none of the code in this module is executed as a result of calls
|
||||
** made on the journal file-handle. */
|
||||
memset(p, 0, sizeof(MemJournal));
|
||||
if( nSpill==0 ){
|
||||
return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
|
||||
}
|
||||
|
||||
if( nSpill>0 ){
|
||||
p->nChunkSize = nSpill;
|
||||
}else{
|
||||
p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
|
||||
assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
|
||||
}
|
||||
|
||||
p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
|
||||
p->nSpill = nSpill;
|
||||
p->flags = flags;
|
||||
p->zJournal = zName;
|
||||
p->pVfs = pVfs;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the file-handle passed as an argument is
|
||||
** an in-memory journal
|
||||
** Open an in-memory journal file.
|
||||
*/
|
||||
int sqlite3IsMemJournal(sqlite3_file *pJfd){
|
||||
return pJfd->pMethods==&MemJournalMethods;
|
||||
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_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.
|
||||
*/
|
||||
int sqlite3JournalCreate(sqlite3_file *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
|
||||
rc = memjrnlCreateFile((MemJournal*)p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The file-handle passed as the only argument is open on a journal file.
|
||||
** Return true if this "journal file" is currently stored in heap memory,
|
||||
** or false otherwise.
|
||||
*/
|
||||
int sqlite3JournalIsInMemory(sqlite3_file *p){
|
||||
return p->pMethods==&MemJournalMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a MemJournal file descriptor.
|
||||
** Return the number of bytes required to store a JournalFile that uses vfs
|
||||
** pVfs to create the underlying on-disk files.
|
||||
*/
|
||||
int sqlite3MemJournalSize(void){
|
||||
return sizeof(MemJournal);
|
||||
int sqlite3JournalSize(sqlite3_vfs *pVfs){
|
||||
return MAX(pVfs->szOsFile, (int)sizeof(MemJournal));
|
||||
}
|
||||
|
||||
+3
-3
@@ -12,8 +12,8 @@
|
||||
**
|
||||
** This file contains code that is specific to MSVC.
|
||||
*/
|
||||
#ifndef _MSVC_H_
|
||||
#define _MSVC_H_
|
||||
#ifndef SQLITE_MSVC_H
|
||||
#define SQLITE_MSVC_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4054)
|
||||
@@ -33,4 +33,4 @@
|
||||
#pragma warning(disable : 4706)
|
||||
#endif /* defined(_MSC_VER) */
|
||||
|
||||
#endif /* _MSVC_H_ */
|
||||
#endif /* SQLITE_MSVC_H */
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
** This file contains OS interface code that is common to all
|
||||
** architectures.
|
||||
*/
|
||||
#define _SQLITE_OS_C_ 1
|
||||
#include "sqliteInt.h"
|
||||
#undef _SQLITE_OS_C_
|
||||
|
||||
/*
|
||||
** If we compile with the SQLITE_TEST macro set, then the following block
|
||||
@@ -66,9 +64,9 @@ int sqlite3_open_file_count = 0;
|
||||
#if defined(SQLITE_TEST)
|
||||
int sqlite3_memdebug_vfs_oom_test = 1;
|
||||
#define DO_OS_MALLOC_TEST(x) \
|
||||
if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \
|
||||
if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
|
||||
void *pTstAlloc = sqlite3Malloc(10); \
|
||||
if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
|
||||
if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \
|
||||
sqlite3_free(pTstAlloc); \
|
||||
}
|
||||
#else
|
||||
@@ -81,13 +79,11 @@ int sqlite3_memdebug_vfs_oom_test = 1;
|
||||
** of this would be completely automatic if SQLite were coded using
|
||||
** C++ instead of plain old C.
|
||||
*/
|
||||
int sqlite3OsClose(sqlite3_file *pId){
|
||||
int rc = SQLITE_OK;
|
||||
void sqlite3OsClose(sqlite3_file *pId){
|
||||
if( pId->pMethods ){
|
||||
rc = pId->pMethods->xClose(pId);
|
||||
pId->pMethods->xClose(pId);
|
||||
pId->pMethods = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
@@ -262,6 +258,9 @@ int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return pVfs->xSleep(pVfs, nMicro);
|
||||
}
|
||||
int sqlite3OsGetLastError(sqlite3_vfs *pVfs){
|
||||
return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0;
|
||||
}
|
||||
int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
|
||||
int rc;
|
||||
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
|
||||
@@ -287,7 +286,7 @@ int sqlite3OsOpenMalloc(
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
int rc;
|
||||
sqlite3_file *pFile;
|
||||
pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
|
||||
if( pFile ){
|
||||
@@ -297,15 +296,15 @@ int sqlite3OsOpenMalloc(
|
||||
}else{
|
||||
*ppFile = pFile;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3OsCloseFree(sqlite3_file *pFile){
|
||||
int rc = SQLITE_OK;
|
||||
void sqlite3OsCloseFree(sqlite3_file *pFile){
|
||||
assert( pFile );
|
||||
rc = sqlite3OsClose(pFile);
|
||||
sqlite3OsClose(pFile);
|
||||
sqlite3_free(pFile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -316,7 +315,7 @@ int sqlite3OsCloseFree(sqlite3_file *pFile){
|
||||
*/
|
||||
int sqlite3OsInit(void){
|
||||
void *p = sqlite3_malloc(10);
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
||||
sqlite3_free(p);
|
||||
return sqlite3_os_init();
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ int sqlite3OsInit(void);
|
||||
/*
|
||||
** Functions for accessing sqlite3_file methods
|
||||
*/
|
||||
int sqlite3OsClose(sqlite3_file*);
|
||||
void sqlite3OsClose(sqlite3_file*);
|
||||
int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
|
||||
int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
|
||||
int sqlite3OsTruncate(sqlite3_file*, i64 size);
|
||||
@@ -197,6 +197,7 @@ void sqlite3OsDlClose(sqlite3_vfs *, void *);
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
|
||||
int sqlite3OsSleep(sqlite3_vfs *, int);
|
||||
int sqlite3OsGetLastError(sqlite3_vfs*);
|
||||
int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
|
||||
|
||||
/*
|
||||
@@ -204,6 +205,6 @@ int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
|
||||
** sqlite3_malloc() to obtain space for the file-handle structure.
|
||||
*/
|
||||
int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
|
||||
int sqlite3OsCloseFree(sqlite3_file *);
|
||||
void sqlite3OsCloseFree(sqlite3_file *);
|
||||
|
||||
#endif /* _SQLITE_OS_H_ */
|
||||
|
||||
+3
-3
@@ -13,8 +13,8 @@
|
||||
** This file contains pre-processor directives related to operating system
|
||||
** detection and/or setup.
|
||||
*/
|
||||
#ifndef _OS_SETUP_H_
|
||||
#define _OS_SETUP_H_
|
||||
#ifndef SQLITE_OS_SETUP_H
|
||||
#define SQLITE_OS_SETUP_H
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other operating
|
||||
@@ -54,4 +54,4 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _OS_SETUP_H_ */
|
||||
#endif /* SQLITE_OS_SETUP_H */
|
||||
|
||||
+104
-62
@@ -71,6 +71,19 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Use pread() and pwrite() if they are available */
|
||||
#if defined(__APPLE__)
|
||||
# define HAVE_PREAD 1
|
||||
# define HAVE_PWRITE 1
|
||||
#endif
|
||||
#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
|
||||
# undef USE_PREAD
|
||||
# define USE_PREAD64 1
|
||||
#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE)
|
||||
# undef USE_PREAD64
|
||||
# define USE_PREAD 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** standard include files.
|
||||
*/
|
||||
@@ -392,7 +405,7 @@ static struct unix_syscall {
|
||||
#else
|
||||
{ "pread64", (sqlite3_syscall_ptr)0, 0 },
|
||||
#endif
|
||||
#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent)
|
||||
#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent)
|
||||
|
||||
{ "write", (sqlite3_syscall_ptr)write, 0 },
|
||||
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
|
||||
@@ -410,7 +423,7 @@ static struct unix_syscall {
|
||||
#else
|
||||
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
|
||||
#endif
|
||||
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
|
||||
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
|
||||
aSyscall[13].pCurrent)
|
||||
|
||||
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
|
||||
@@ -1302,7 +1315,7 @@ static int findInodeInfo(
|
||||
if( pInode==0 ){
|
||||
pInode = sqlite3_malloc64( sizeof(*pInode) );
|
||||
if( pInode==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memset(pInode, 0, sizeof(*pInode));
|
||||
memcpy(&pInode->fileId, &fileId, sizeof(fileId));
|
||||
@@ -1344,12 +1357,16 @@ static int fileHasMoved(unixFile *pFile){
|
||||
static void verifyDbFile(unixFile *pFile){
|
||||
struct stat buf;
|
||||
int rc;
|
||||
|
||||
/* These verifications occurs for the main database only */
|
||||
if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return;
|
||||
|
||||
rc = osFstat(pFile->h, &buf);
|
||||
if( rc!=0 ){
|
||||
sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
|
||||
return;
|
||||
}
|
||||
if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){
|
||||
if( buf.st_nlink==0 ){
|
||||
sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
|
||||
return;
|
||||
}
|
||||
@@ -1485,7 +1502,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
|
||||
** lock transitions in terms of the POSIX advisory shared and exclusive
|
||||
** lock primitives (called read-locks and write-locks below, to avoid
|
||||
** confusion with SQLite lock names). The algorithms are complicated
|
||||
** slightly in order to be compatible with windows systems simultaneously
|
||||
** slightly in order to be compatible with Windows95 systems simultaneously
|
||||
** accessing the same database file, in case that is ever required.
|
||||
**
|
||||
** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
|
||||
@@ -1493,8 +1510,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
|
||||
** range', a range of 510 bytes at a well known offset.
|
||||
**
|
||||
** To obtain a SHARED lock, a read-lock is obtained on the 'pending
|
||||
** byte'. If this is successful, a random byte from the 'shared byte
|
||||
** range' is read-locked and the lock on the 'pending byte' released.
|
||||
** byte'. If this is successful, 'shared byte range' is read-locked
|
||||
** and the lock on the 'pending byte' released. (Legacy note: When
|
||||
** SQLite was first developed, Windows95 systems were still very common,
|
||||
** and Widnows95 lacks a shared-lock capability. So on Windows95, a
|
||||
** single randomly selected by from the 'shared byte range' is locked.
|
||||
** Windows95 is now pretty much extinct, but this work-around for the
|
||||
** lack of shared-locks on Windows95 lives on, for backwards
|
||||
** compatibility.)
|
||||
**
|
||||
** A process may only obtain a RESERVED lock after it has a SHARED lock.
|
||||
** A RESERVED lock is implemented by grabbing a write-lock on the
|
||||
@@ -1513,11 +1536,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){
|
||||
** range'. Since all other locks require a read-lock on one of the bytes
|
||||
** within this range, this ensures that no other locks are held on the
|
||||
** database.
|
||||
**
|
||||
** The reason a single byte cannot be used instead of the 'shared byte
|
||||
** range' is that some versions of windows do not support read-locks. By
|
||||
** locking a random byte from a range, concurrent SHARED locks may exist
|
||||
** even if the locking primitive used is always a write-lock.
|
||||
*/
|
||||
int rc = SQLITE_OK;
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
@@ -4222,7 +4240,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
|
||||
/* Allocate space for the new unixShm object. */
|
||||
p = sqlite3_malloc64( sizeof(*p) );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
||||
memset(p, 0, sizeof(*p));
|
||||
assert( pDbFd->pShm==0 );
|
||||
|
||||
@@ -4254,7 +4272,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
#endif
|
||||
pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
|
||||
if( pShmNode==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto shm_open_err;
|
||||
}
|
||||
memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
|
||||
@@ -4270,10 +4288,12 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
pShmNode->h = -1;
|
||||
pDbFd->pInode->pShmNode = pShmNode;
|
||||
pShmNode->pInode = pDbFd->pInode;
|
||||
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
if( pShmNode->mutex==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto shm_open_err;
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
if( pShmNode->mutex==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto shm_open_err;
|
||||
}
|
||||
}
|
||||
|
||||
if( pInode->bProcessLock==0 ){
|
||||
@@ -4445,7 +4465,7 @@ static int unixShmMap(
|
||||
pShmNode->apRegion, nReqRegion*sizeof(char *)
|
||||
);
|
||||
if( !apNew ){
|
||||
rc = SQLITE_IOERR_NOMEM;
|
||||
rc = SQLITE_IOERR_NOMEM_BKPT;
|
||||
goto shmpage_out;
|
||||
}
|
||||
pShmNode->apRegion = apNew;
|
||||
@@ -4465,7 +4485,7 @@ static int unixShmMap(
|
||||
}else{
|
||||
pMem = sqlite3_malloc64(szRegion);
|
||||
if( pMem==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto shmpage_out;
|
||||
}
|
||||
memset(pMem, 0, szRegion);
|
||||
@@ -5243,7 +5263,7 @@ static int fillInUnixFile(
|
||||
pNew->pId = vxworksFindFileId(zFilename);
|
||||
if( pNew->pId==0 ){
|
||||
ctrlFlags |= UNIXFILE_NOLOCK;
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5299,7 +5319,7 @@ static int fillInUnixFile(
|
||||
afpLockingContext *pCtx;
|
||||
pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
||||
if( pCtx==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
/* NB: zFilename exists and remains valid until the file is closed
|
||||
** according to requirement F11141. So we do not need to make a
|
||||
@@ -5329,7 +5349,7 @@ static int fillInUnixFile(
|
||||
nFilename = (int)strlen(zFilename) + 6;
|
||||
zLockFile = (char *)sqlite3_malloc64(nFilename);
|
||||
if( zLockFile==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename);
|
||||
}
|
||||
@@ -5352,7 +5372,7 @@ static int fillInUnixFile(
|
||||
if( zSemName[n]=='/' ) zSemName[n] = '_';
|
||||
pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1);
|
||||
if( pNew->pInode->pSem == SEM_FAILED ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
pNew->pInode->aSemName[0] = '\0';
|
||||
}
|
||||
}
|
||||
@@ -5392,20 +5412,24 @@ static const char *unixTempFileDir(void){
|
||||
"/tmp",
|
||||
"."
|
||||
};
|
||||
unsigned int i;
|
||||
unsigned int i = 0;
|
||||
struct stat buf;
|
||||
const char *zDir = sqlite3_temp_directory;
|
||||
|
||||
if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
|
||||
if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
||||
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
||||
if( zDir==0 ) continue;
|
||||
if( osStat(zDir, &buf) ) continue;
|
||||
if( !S_ISDIR(buf.st_mode) ) continue;
|
||||
if( osAccess(zDir, 07) ) continue;
|
||||
break;
|
||||
while(1){
|
||||
if( zDir!=0
|
||||
&& osStat(zDir, &buf)==0
|
||||
&& S_ISDIR(buf.st_mode)
|
||||
&& osAccess(zDir, 03)==0
|
||||
){
|
||||
return zDir;
|
||||
}
|
||||
if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break;
|
||||
zDir = azDirs[i++];
|
||||
}
|
||||
return zDir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5421,9 +5445,11 @@ static int unixGetTempname(int nBuf, char *zBuf){
|
||||
** using the io-error infrastructure to test that SQLite handles this
|
||||
** function failing.
|
||||
*/
|
||||
zBuf[0] = 0;
|
||||
SimulateIOError( return SQLITE_IOERR );
|
||||
|
||||
zDir = unixTempFileDir();
|
||||
if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
|
||||
do{
|
||||
u64 r;
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
@@ -5503,6 +5529,27 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
return pUnused;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the mode, uid and gid of file zFile.
|
||||
*/
|
||||
static int getFileMode(
|
||||
const char *zFile, /* File name */
|
||||
mode_t *pMode, /* OUT: Permissions of zFile */
|
||||
uid_t *pUid, /* OUT: uid of zFile. */
|
||||
gid_t *pGid /* OUT: gid of zFile. */
|
||||
){
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
int rc = SQLITE_OK;
|
||||
if( 0==osStat(zFile, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by unixOpen() to determine the unix permissions
|
||||
** to create new files with. If no error occurs, then SQLITE_OK is returned
|
||||
@@ -5538,7 +5585,6 @@ static int findCreateFileMode(
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
char zDb[MAX_PATHNAME+1]; /* Database file path */
|
||||
int nDb; /* Number of valid bytes in zDb */
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
|
||||
/* zPath is a path to a WAL or journal file. The following block derives
|
||||
** the path to the associated database file from zPath. This block handles
|
||||
@@ -5569,15 +5615,18 @@ static int findCreateFileMode(
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
if( 0==osStat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
rc = getFileMode(zDb, pMode, pUid, pGid);
|
||||
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
*pMode = 0600;
|
||||
}else if( flags & SQLITE_OPEN_URI ){
|
||||
/* If this is a main database file and the file was opened using a URI
|
||||
** filename, check for the "modeof" parameter. If present, interpret
|
||||
** its value as a filename and try to copy the mode, uid and gid from
|
||||
** that file. */
|
||||
const char *z = sqlite3_uri_parameter(zPath, "modeof");
|
||||
if( z ){
|
||||
rc = getFileMode(z, pMode, pUid, pGid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -5693,7 +5742,7 @@ static int unixOpen(
|
||||
}else{
|
||||
pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
||||
if( !pUnused ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
p->pUnused = pUnused;
|
||||
@@ -5779,7 +5828,7 @@ static int unixOpen(
|
||||
zPath = sqlite3_mprintf("%s", zName);
|
||||
if( zPath==0 ){
|
||||
robust_close(p, fd, __LINE__);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
#else
|
||||
osUnlink(zName);
|
||||
@@ -5790,9 +5839,6 @@ static int unixOpen(
|
||||
p->openFlags = openFlags;
|
||||
}
|
||||
#endif
|
||||
|
||||
noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
||||
|
||||
|
||||
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
if( fstatfs(fd, &fsInfo) == -1 ){
|
||||
@@ -5811,6 +5857,7 @@ static int unixOpen(
|
||||
/* Set up appropriate ctrlFlags */
|
||||
if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
|
||||
if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
|
||||
noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
||||
if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
|
||||
if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
||||
if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
|
||||
@@ -6011,7 +6058,7 @@ static int unixFullPathname(
|
||||
if( bLink ){
|
||||
if( zDel==0 ){
|
||||
zDel = sqlite3_malloc(nOut);
|
||||
if( zDel==0 ) rc = SQLITE_NOMEM;
|
||||
if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( ++nLink>SQLITE_MAX_SYMLINKS ){
|
||||
rc = SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
@@ -6249,23 +6296,18 @@ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
||||
# define unixCurrentTime 0
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** We added the xGetLastError() method with the intention of providing
|
||||
** better low-level error messages when operating-system problems come up
|
||||
** during SQLite operation. But so far, none of that has been implemented
|
||||
** in the core. So this routine is never called. For now, it is merely
|
||||
** a place-holder.
|
||||
** The xGetLastError() method is designed to return a better
|
||||
** low-level error message when operating-system problems come up
|
||||
** during SQLite operation. Only the integer return code is currently
|
||||
** used.
|
||||
*/
|
||||
static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
UNUSED_PARAMETER(NotUsed2);
|
||||
UNUSED_PARAMETER(NotUsed3);
|
||||
return 0;
|
||||
return errno;
|
||||
}
|
||||
#else
|
||||
# define unixGetLastError 0
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -6555,7 +6597,7 @@ static int proxyCreateUnixFile(
|
||||
}else{
|
||||
pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
||||
if( !pUnused ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
if( fd<0 ){
|
||||
@@ -6588,7 +6630,7 @@ static int proxyCreateUnixFile(
|
||||
|
||||
pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
|
||||
if( pNew==NULL ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto end_create_proxy;
|
||||
}
|
||||
memset(pNew, 0, sizeof(unixFile));
|
||||
@@ -7001,7 +7043,7 @@ static int proxyTakeConch(unixFile *pFile){
|
||||
if( tempLockPath ){
|
||||
pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
|
||||
if( !pCtx->lockProxyPath ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7066,7 +7108,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
|
||||
** the name of the original database file. */
|
||||
*pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
|
||||
if( conchPath==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memcpy(conchPath, dbPath, len+1);
|
||||
|
||||
@@ -7182,7 +7224,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
||||
|
||||
pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
||||
if( pCtx==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memset(pCtx, 0, sizeof(*pCtx));
|
||||
|
||||
@@ -7218,7 +7260,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
||||
if( rc==SQLITE_OK ){
|
||||
pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
|
||||
if( pCtx->dbPath==NULL ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
+528
-224
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -12,8 +12,8 @@
|
||||
**
|
||||
** This file contains code that is specific to Windows.
|
||||
*/
|
||||
#ifndef _OS_WIN_H_
|
||||
#define _OS_WIN_H_
|
||||
#ifndef SQLITE_OS_WIN_H
|
||||
#define SQLITE_OS_WIN_H
|
||||
|
||||
/*
|
||||
** Include the primary Windows SDK header file.
|
||||
@@ -85,4 +85,4 @@
|
||||
# define SQLITE_OS_WIN_THREADS 0
|
||||
#endif
|
||||
|
||||
#endif /* _OS_WIN_H_ */
|
||||
#endif /* SQLITE_OS_WIN_H */
|
||||
|
||||
+171
-144
@@ -428,19 +428,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
|
||||
*/
|
||||
#define MAX_SECTOR_SIZE 0x10000
|
||||
|
||||
/*
|
||||
** If the option SQLITE_EXTRA_DURABLE option is set at compile-time, then
|
||||
** SQLite will do extra fsync() operations when synchronous==FULL to help
|
||||
** ensure that transactions are durable across a power failure. Most
|
||||
** applications are happy as long as transactions are consistent across
|
||||
** a power failure, and are perfectly willing to lose the last transaction
|
||||
** in exchange for the extra performance of avoiding directory syncs.
|
||||
** And so the default SQLITE_EXTRA_DURABLE setting is off.
|
||||
*/
|
||||
#ifndef SQLITE_EXTRA_DURABLE
|
||||
# define SQLITE_EXTRA_DURABLE 0
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** An instance of the following structure is allocated for each active
|
||||
@@ -830,9 +817,10 @@ static const unsigned char aJournalMagic[] = {
|
||||
** rollback journal. Otherwise false.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
static int pagerUseWal(Pager *pPager){
|
||||
int sqlite3PagerUseWal(Pager *pPager){
|
||||
return (pPager->pWal!=0);
|
||||
}
|
||||
# define pagerUseWal(x) sqlite3PagerUseWal(x)
|
||||
#else
|
||||
# define pagerUseWal(x) 0
|
||||
# define pagerRollbackWal(x) 0
|
||||
@@ -885,6 +873,7 @@ static int assert_pager_state(Pager *p){
|
||||
** state.
|
||||
*/
|
||||
if( MEMDB ){
|
||||
assert( !isOpen(p->fd) );
|
||||
assert( p->noSync );
|
||||
assert( p->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| p->journalMode==PAGER_JOURNALMODE_MEMORY
|
||||
@@ -971,7 +960,7 @@ static int assert_pager_state(Pager *p){
|
||||
** back to OPEN state.
|
||||
*/
|
||||
assert( pPager->errCode!=SQLITE_OK );
|
||||
assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
|
||||
assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1183,6 +1172,8 @@ static int jrnlBufferSize(Pager *pPager){
|
||||
|
||||
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
|
||||
}
|
||||
#else
|
||||
# define jrnlBufferSize(x) 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1343,6 +1334,7 @@ static i64 journalHdrOffset(Pager *pPager){
|
||||
static int zeroJournalHdr(Pager *pPager, int doTruncate){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
assert( isOpen(pPager->jfd) );
|
||||
assert( !sqlite3JournalIsInMemory(pPager->jfd) );
|
||||
if( pPager->journalOff ){
|
||||
const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */
|
||||
|
||||
@@ -1724,7 +1716,7 @@ static void releaseAllSavepoints(Pager *pPager){
|
||||
for(ii=0; ii<pPager->nSavepoint; ii++){
|
||||
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
|
||||
}
|
||||
if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
|
||||
if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){
|
||||
sqlite3OsClose(pPager->sjfd);
|
||||
}
|
||||
sqlite3_free(pPager->aSavepoint);
|
||||
@@ -1830,13 +1822,17 @@ static void pager_unlock(Pager *pPager){
|
||||
** it can safely move back to PAGER_OPEN state. This happens in both
|
||||
** normal and exclusive-locking mode.
|
||||
*/
|
||||
assert( pPager->errCode==SQLITE_OK || !MEMDB );
|
||||
if( pPager->errCode ){
|
||||
assert( !MEMDB );
|
||||
pager_reset(pPager);
|
||||
pPager->changeCountDone = pPager->tempFile;
|
||||
pPager->eState = PAGER_OPEN;
|
||||
pPager->errCode = SQLITE_OK;
|
||||
if( pPager->tempFile==0 ){
|
||||
pager_reset(pPager);
|
||||
pPager->changeCountDone = 0;
|
||||
pPager->eState = PAGER_OPEN;
|
||||
}else{
|
||||
pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER);
|
||||
}
|
||||
if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
|
||||
pPager->errCode = SQLITE_OK;
|
||||
}
|
||||
|
||||
pPager->journalOff = 0;
|
||||
@@ -1880,6 +1876,29 @@ static int pager_error(Pager *pPager, int rc){
|
||||
|
||||
static int pager_truncate(Pager *pPager, Pgno nPage);
|
||||
|
||||
/*
|
||||
** The write transaction open on pPager is being committed (bCommit==1)
|
||||
** or rolled back (bCommit==0).
|
||||
**
|
||||
** Return TRUE if and only if all dirty pages should be flushed to disk.
|
||||
**
|
||||
** Rules:
|
||||
**
|
||||
** * For non-TEMP databases, always sync to disk. This is necessary
|
||||
** for transactions to be durable.
|
||||
**
|
||||
** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing
|
||||
** file has been created already (via a spill on pagerStress()) and
|
||||
** when the number of dirty pages in memory exceeds 25% of the total
|
||||
** cache size.
|
||||
*/
|
||||
static int pagerFlushOnCommit(Pager *pPager, int bCommit){
|
||||
if( pPager->tempFile==0 ) return 1;
|
||||
if( !bCommit ) return 0;
|
||||
if( !isOpen(pPager->fd) ) return 0;
|
||||
return (sqlite3PCachePercentDirty(pPager->pPCache)>=25);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine ends a transaction. A transaction is usually ended by
|
||||
** either a COMMIT or a ROLLBACK operation. This routine may be called
|
||||
@@ -1962,8 +1981,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
assert( !pagerUseWal(pPager) );
|
||||
|
||||
/* Finalize the journal file. */
|
||||
if( sqlite3IsMemJournal(pPager->jfd) ){
|
||||
assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
|
||||
if( sqlite3JournalIsInMemory(pPager->jfd) ){
|
||||
/* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
|
||||
if( pPager->journalOff==0 ){
|
||||
@@ -1983,15 +2002,16 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|
||||
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
|
||||
){
|
||||
rc = zeroJournalHdr(pPager, hasMaster);
|
||||
rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile);
|
||||
pPager->journalOff = 0;
|
||||
}else{
|
||||
/* This branch may be executed with Pager.journalMode==MEMORY if
|
||||
** a hot-journal was just rolled back. In this case the journal
|
||||
** file should be closed and deleted. If this connection writes to
|
||||
** the database file, it will do so using an in-memory journal.
|
||||
** the database file, it will do so using an in-memory journal.
|
||||
*/
|
||||
int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd));
|
||||
int bDelete = !pPager->tempFile;
|
||||
assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
|
||||
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|
||||
@@ -2017,8 +2037,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
sqlite3BitvecDestroy(pPager->pInJournal);
|
||||
pPager->pInJournal = 0;
|
||||
pPager->nRec = 0;
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pagerFlushOnCommit(pPager, bCommit) ){
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}else{
|
||||
sqlite3PcacheClearWritable(pPager->pPCache);
|
||||
}
|
||||
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
|
||||
}
|
||||
|
||||
if( pagerUseWal(pPager) ){
|
||||
/* Drop the WAL write-lock, if any. Also, if the connection was in
|
||||
@@ -2302,7 +2328,7 @@ static int pager_playback_one_page(
|
||||
pPg = sqlite3PagerLookup(pPager, pgno);
|
||||
}
|
||||
assert( pPg || !MEMDB );
|
||||
assert( pPager->eState!=PAGER_OPEN || pPg==0 );
|
||||
assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile );
|
||||
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
|
||||
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
|
||||
(isMainJrnl?"main-journal":"sub-journal")
|
||||
@@ -2324,9 +2350,9 @@ static int pager_playback_one_page(
|
||||
pPager->dbFileSize = pgno;
|
||||
}
|
||||
if( pPager->pBackup ){
|
||||
CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM);
|
||||
CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT);
|
||||
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData);
|
||||
CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData);
|
||||
CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData);
|
||||
}
|
||||
}else if( !isMainJrnl && pPg==0 ){
|
||||
/* If this is a rollback of a savepoint and data was not written to
|
||||
@@ -2352,7 +2378,6 @@ static int pager_playback_one_page(
|
||||
assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
|
||||
pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
pPg->flags &= ~PGHDR_NEED_READ;
|
||||
sqlite3PcacheMakeDirty(pPg);
|
||||
}
|
||||
if( pPg ){
|
||||
@@ -2366,29 +2391,10 @@ static int pager_playback_one_page(
|
||||
pData = pPg->pData;
|
||||
memcpy(pData, (u8*)aData, pPager->pageSize);
|
||||
pPager->xReiniter(pPg);
|
||||
if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){
|
||||
/* If the contents of this page were just restored from the main
|
||||
** journal file, then its content must be as they were when the
|
||||
** transaction was first opened. In this case we can mark the page
|
||||
** as clean, since there will be no need to write it out to the
|
||||
** database.
|
||||
**
|
||||
** There is one exception to this rule. If the page is being rolled
|
||||
** back as part of a savepoint (or statement) rollback from an
|
||||
** unsynced portion of the main journal file, then it is not safe
|
||||
** to mark the page as clean. This is because marking the page as
|
||||
** clean will clear the PGHDR_NEED_SYNC flag. Since the page is
|
||||
** already in the journal file (recorded in Pager.pInJournal) and
|
||||
** the PGHDR_NEED_SYNC flag is cleared, if the page is written to
|
||||
** again within this transaction, it will be marked as dirty but
|
||||
** the PGHDR_NEED_SYNC flag will not be set. It could then potentially
|
||||
** be written out into the database file before its journal file
|
||||
** segment is synced. If a crash occurs during or following this,
|
||||
** database corruption may ensue.
|
||||
*/
|
||||
assert( !pagerUseWal(pPager) );
|
||||
sqlite3PcacheMakeClean(pPg);
|
||||
}
|
||||
/* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But
|
||||
** that call was dangerous and had no detectable benefit since the cache
|
||||
** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so
|
||||
** has been removed. */
|
||||
pager_set_pagehash(pPg);
|
||||
|
||||
/* If this was page 1, then restore the value of Pager.dbFileVers.
|
||||
@@ -2398,7 +2404,7 @@ static int pager_playback_one_page(
|
||||
}
|
||||
|
||||
/* Decode the page just read from disk */
|
||||
CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM);
|
||||
CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT);
|
||||
sqlite3PcacheRelease(pPg);
|
||||
}
|
||||
return rc;
|
||||
@@ -2464,7 +2470,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
|
||||
pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
|
||||
pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
|
||||
if( !pMaster ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
|
||||
rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
|
||||
@@ -2481,7 +2487,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
|
||||
nMasterPtr = pVfs->mxPathname+1;
|
||||
zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
|
||||
if( !zMasterJournal ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto delmaster_out;
|
||||
}
|
||||
zMasterPtr = &zMasterJournal[nMasterJournal+1];
|
||||
@@ -2729,7 +2735,7 @@ static int pager_playback(Pager *pPager, int isHot){
|
||||
** TODO: Technically the following is an error because it assumes that
|
||||
** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
|
||||
** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
|
||||
** mxPathname is 512, which is the same as the minimum allowable value
|
||||
** mxPathname is 512, which is the same as the minimum allowable value
|
||||
** for pageSize.
|
||||
*/
|
||||
zMaster = pPager->pTmpSpace;
|
||||
@@ -2951,7 +2957,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
|
||||
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
|
||||
}
|
||||
}
|
||||
CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM);
|
||||
CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT);
|
||||
|
||||
PAGER_INCR(sqlite3_pager_readdb_count);
|
||||
PAGER_INCR(pPager->nRead);
|
||||
@@ -3179,6 +3185,8 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
||||
*/
|
||||
assert( pPager->eState==PAGER_OPEN );
|
||||
assert( pPager->eLock>=SHARED_LOCK );
|
||||
assert( isOpen(pPager->fd) );
|
||||
assert( pPager->tempFile==0 );
|
||||
nPage = sqlite3WalDbsize(pPager->pWal);
|
||||
|
||||
/* If the number of pages in the database is not available from the
|
||||
@@ -3186,14 +3194,11 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
||||
** the database file. If the size of the database file is not an
|
||||
** integer multiple of the page-size, round up the result.
|
||||
*/
|
||||
if( nPage==0 ){
|
||||
if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){
|
||||
i64 n = 0; /* Size of db file in bytes */
|
||||
assert( isOpen(pPager->fd) || pPager->tempFile );
|
||||
if( isOpen(pPager->fd) ){
|
||||
int rc = sqlite3OsFileSize(pPager->fd, &n);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
int rc = sqlite3OsFileSize(pPager->fd, &n);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize);
|
||||
}
|
||||
@@ -3311,7 +3316,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
|
||||
if( pSavepoint ){
|
||||
pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
|
||||
if( !pDone ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3458,7 +3463,7 @@ void sqlite3PagerShrink(Pager *pPager){
|
||||
** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
|
||||
** of the database to damage due to OS crashes or power failures by
|
||||
** changing the number of syncs()s when writing the journals.
|
||||
** There are three levels:
|
||||
** There are four levels:
|
||||
**
|
||||
** OFF sqlite3OsSync() is never called. This is the default
|
||||
** for temporary and transient files.
|
||||
@@ -3478,6 +3483,10 @@ void sqlite3PagerShrink(Pager *pPager){
|
||||
** assurance that the journal will not be corrupted to the
|
||||
** point of causing damage to the database during rollback.
|
||||
**
|
||||
** EXTRA This is like FULL except that is also syncs the directory
|
||||
** that contains the rollback journal after the rollback
|
||||
** journal is unlinked.
|
||||
**
|
||||
** The above is for a rollback-journal mode. For WAL mode, OFF continues
|
||||
** to mean that no syncs ever occur. NORMAL means that the WAL is synced
|
||||
** prior to the start of checkpoint and that the database file is synced
|
||||
@@ -3485,7 +3494,8 @@ void sqlite3PagerShrink(Pager *pPager){
|
||||
** was written back into the database. But no sync operations occur for
|
||||
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL
|
||||
** file is synced following each commit operation, in addition to the
|
||||
** syncs associated with NORMAL.
|
||||
** syncs associated with NORMAL. There is no difference between FULL
|
||||
** and EXTRA for WAL mode.
|
||||
**
|
||||
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
|
||||
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
|
||||
@@ -3674,7 +3684,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = (char *)sqlite3PageMalloc(pageSize);
|
||||
if( !pNew ) rc = SQLITE_NOMEM;
|
||||
if( !pNew ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -3950,7 +3960,7 @@ static int pagerAcquireMapPage(
|
||||
*ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra);
|
||||
if( p==0 ){
|
||||
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
p->pExtra = (void *)&p[1];
|
||||
p->flags = PGHDR_MMAP;
|
||||
@@ -4264,8 +4274,9 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
|
||||
/* This function is only called for rollback pagers in WRITER_DBMOD state. */
|
||||
assert( !pagerUseWal(pPager) );
|
||||
assert( pPager->eState==PAGER_WRITER_DBMOD );
|
||||
assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD );
|
||||
assert( pPager->eLock==EXCLUSIVE_LOCK );
|
||||
assert( isOpen(pPager->fd) || pList->pDirty==0 );
|
||||
|
||||
/* If the file is a temp-file has not yet been opened, open it now. It
|
||||
** is not possible for rc to be other than SQLITE_OK if this branch
|
||||
@@ -4308,7 +4319,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
|
||||
/* Encode the database */
|
||||
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
|
||||
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData);
|
||||
|
||||
/* Write out the page data. */
|
||||
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
|
||||
@@ -4353,11 +4364,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
static int openSubJournal(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
if( !isOpen(pPager->sjfd) ){
|
||||
const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
|
||||
| SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
|
||||
| SQLITE_OPEN_DELETEONCLOSE;
|
||||
int nStmtSpill = sqlite3Config.nStmtSpill;
|
||||
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
|
||||
sqlite3MemJournalOpen(pPager->sjfd);
|
||||
}else{
|
||||
rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
|
||||
nStmtSpill = -1;
|
||||
}
|
||||
rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -4395,7 +4409,7 @@ static int subjournalPage(PgHdr *pPg){
|
||||
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
|
||||
char *pData2;
|
||||
|
||||
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
||||
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
|
||||
PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno));
|
||||
rc = write32bits(pPager->sjfd, offset, pPg->pgno);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -4578,18 +4592,8 @@ int sqlite3PagerOpen(
|
||||
int nUri = 0; /* Number of bytes of URI args at *zUri */
|
||||
|
||||
/* Figure out how much space is required for each journal file-handle
|
||||
** (there are two of them, the main journal and the sub-journal). This
|
||||
** is the maximum space required for an in-memory journal file handle
|
||||
** and a regular journal file-handle. Note that a "regular journal-handle"
|
||||
** may be a wrapper capable of caching the first portion of the journal
|
||||
** file in memory to implement the atomic-write optimization (see
|
||||
** source file journal.c).
|
||||
*/
|
||||
if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
|
||||
journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
|
||||
}else{
|
||||
journalFileSize = ROUND8(sqlite3MemJournalSize());
|
||||
}
|
||||
** (there are two of them, the main journal and the sub-journal). */
|
||||
journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
|
||||
|
||||
/* Set the output variable to NULL in case an error occurs. */
|
||||
*ppPager = 0;
|
||||
@@ -4599,7 +4603,7 @@ int sqlite3PagerOpen(
|
||||
memDb = 1;
|
||||
if( zFilename && zFilename[0] ){
|
||||
zPathname = sqlite3DbStrDup(0, zFilename);
|
||||
if( zPathname==0 ) return SQLITE_NOMEM;
|
||||
if( zPathname==0 ) return SQLITE_NOMEM_BKPT;
|
||||
nPathname = sqlite3Strlen30(zPathname);
|
||||
zFilename = 0;
|
||||
}
|
||||
@@ -4615,7 +4619,7 @@ int sqlite3PagerOpen(
|
||||
nPathname = pVfs->mxPathname+1;
|
||||
zPathname = sqlite3DbMallocRaw(0, nPathname*2);
|
||||
if( zPathname==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
|
||||
@@ -4668,7 +4672,7 @@ int sqlite3PagerOpen(
|
||||
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
|
||||
if( !pPtr ){
|
||||
sqlite3DbFree(0, zPathname);
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pPager = (Pager*)(pPtr);
|
||||
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
|
||||
@@ -4823,11 +4827,7 @@ act_like_temp_file:
|
||||
assert( pPager->ckptSyncFlags==0 );
|
||||
}else{
|
||||
pPager->fullSync = 1;
|
||||
#if SQLITE_EXTRA_DURABLE
|
||||
pPager->extraSync = 1;
|
||||
#else
|
||||
pPager->extraSync = 0;
|
||||
#endif
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
@@ -4944,6 +4944,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
|
||||
if( rc==SQLITE_OK && !locked ){
|
||||
Pgno nPage; /* Number of pages in database file */
|
||||
|
||||
assert( pPager->tempFile==0 );
|
||||
rc = pagerPagecount(pPager, &nPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If the database is zero pages in size, that means that either (1) the
|
||||
@@ -5036,17 +5037,17 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
/* This routine is only called from b-tree and only when there are no
|
||||
** outstanding pages. This implies that the pager state should either
|
||||
** be OPEN or READER. READER is only possible if the pager is or was in
|
||||
** exclusive access mode.
|
||||
*/
|
||||
** exclusive access mode. */
|
||||
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
|
||||
assert( assert_pager_state(pPager) );
|
||||
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
|
||||
if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
|
||||
assert( pPager->errCode==SQLITE_OK );
|
||||
|
||||
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
|
||||
int bHotJournal = 1; /* True if there exists a hot journal-file */
|
||||
|
||||
assert( !MEMDB );
|
||||
assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
|
||||
|
||||
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -5132,7 +5133,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = pagerSyncHotJournal(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_playback(pPager, 1);
|
||||
rc = pager_playback(pPager, !pPager->tempFile);
|
||||
pPager->eState = PAGER_OPEN;
|
||||
}
|
||||
}else if( !pPager->exclusiveMode ){
|
||||
@@ -5228,7 +5229,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
rc = pagerBeginReadTransaction(pPager);
|
||||
}
|
||||
|
||||
if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
|
||||
if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
|
||||
rc = pagerPagecount(pPager, &pPager->dbSize);
|
||||
}
|
||||
|
||||
@@ -5361,7 +5362,7 @@ int sqlite3PagerGet(
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK && pData ){
|
||||
if( pPager->eState>PAGER_READER ){
|
||||
if( pPager->eState>PAGER_READER || pPager->tempFile ){
|
||||
pPg = sqlite3PagerLookup(pPager, pgno);
|
||||
}
|
||||
if( pPg==0 ){
|
||||
@@ -5388,7 +5389,7 @@ int sqlite3PagerGet(
|
||||
if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
||||
if( pBase==0 ){
|
||||
pPg = *ppPage = 0;
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto pager_acquire_err;
|
||||
}
|
||||
}
|
||||
@@ -5428,7 +5429,8 @@ int sqlite3PagerGet(
|
||||
goto pager_acquire_err;
|
||||
}
|
||||
|
||||
if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
|
||||
assert( !isOpen(pPager->fd) || !MEMDB );
|
||||
if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
|
||||
if( pgno>pPager->mxPgno ){
|
||||
rc = SQLITE_FULL;
|
||||
goto pager_acquire_err;
|
||||
@@ -5562,7 +5564,7 @@ static int pager_open_journal(Pager *pPager){
|
||||
if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
|
||||
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
|
||||
if( pPager->pInJournal==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
/* Open the journal file if it is not already open. */
|
||||
@@ -5570,24 +5572,24 @@ static int pager_open_journal(Pager *pPager){
|
||||
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
|
||||
sqlite3MemJournalOpen(pPager->jfd);
|
||||
}else{
|
||||
const int flags = /* VFS flags to open journal file */
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
|
||||
(pPager->tempFile ?
|
||||
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
|
||||
(SQLITE_OPEN_MAIN_JOURNAL)
|
||||
);
|
||||
int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
int nSpill;
|
||||
|
||||
if( pPager->tempFile ){
|
||||
flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
|
||||
nSpill = sqlite3Config.nStmtSpill;
|
||||
}else{
|
||||
flags |= SQLITE_OPEN_MAIN_JOURNAL;
|
||||
nSpill = jrnlBufferSize(pPager);
|
||||
}
|
||||
|
||||
/* Verify that the database still has the same name as it did when
|
||||
** it was originally opened. */
|
||||
rc = databaseIsUnmoved(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
rc = sqlite3JournalOpen(
|
||||
pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
|
||||
rc = sqlite3JournalOpen (
|
||||
pVfs, pPager->zJournal, pPager->jfd, flags, nSpill
|
||||
);
|
||||
#else
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
|
||||
@@ -5717,7 +5719,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
|
||||
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
|
||||
assert( pPager->journalHdr<=pPager->journalOff );
|
||||
CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
||||
CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
|
||||
cksum = pager_cksum(pPager, (u8*)pData2);
|
||||
|
||||
/* Even if an IO or diskfull error occurs while journalling the
|
||||
@@ -5958,6 +5960,7 @@ int sqlite3PagerWrite(PgHdr *pPg){
|
||||
if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg);
|
||||
return SQLITE_OK;
|
||||
}else if( pPager->sectorSize > (u32)pPager->pageSize ){
|
||||
assert( pPager->tempFile==0 );
|
||||
return pagerWriteLargeSector(pPg);
|
||||
}else{
|
||||
return pager_write(pPg);
|
||||
@@ -5988,14 +5991,21 @@ int sqlite3PagerIswriteable(DbPage *pPg){
|
||||
**
|
||||
** Tests show that this optimization can quadruple the speed of large
|
||||
** DELETE operations.
|
||||
**
|
||||
** This optimization cannot be used with a temp-file, as the page may
|
||||
** have been dirty at the start of the transaction. In that case, if
|
||||
** memory pressure forces page pPg out of the cache, the data does need
|
||||
** to be written out to disk so that it may be read back in if the
|
||||
** current transaction is rolled back.
|
||||
*/
|
||||
void sqlite3PagerDontWrite(PgHdr *pPg){
|
||||
Pager *pPager = pPg->pPager;
|
||||
if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
|
||||
if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
|
||||
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
|
||||
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
|
||||
pPg->flags |= PGHDR_DONT_WRITE;
|
||||
pPg->flags &= ~PGHDR_WRITEABLE;
|
||||
testcase( pPg->flags & PGHDR_NEED_SYNC );
|
||||
pager_set_pagehash(pPg);
|
||||
}
|
||||
}
|
||||
@@ -6074,7 +6084,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
if( DIRECT_MODE ){
|
||||
const void *zBuf;
|
||||
assert( pPager->dbFileSize>0 );
|
||||
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
|
||||
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
|
||||
pPager->aStat[PAGER_STAT_WRITE]++;
|
||||
@@ -6190,17 +6200,21 @@ int sqlite3PagerCommitPhaseOne(
|
||||
/* If a prior error occurred, report that error again. */
|
||||
if( NEVER(pPager->errCode) ) return pPager->errCode;
|
||||
|
||||
/* Provide the ability to easily simulate an I/O error during testing */
|
||||
if( sqlite3FaultSim(400) ) return SQLITE_IOERR;
|
||||
|
||||
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
|
||||
pPager->zFilename, zMaster, pPager->dbSize));
|
||||
|
||||
/* If no database changes have been made, return early. */
|
||||
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
|
||||
|
||||
if( MEMDB ){
|
||||
assert( MEMDB==0 || pPager->tempFile );
|
||||
assert( isOpen(pPager->fd) || pPager->tempFile );
|
||||
if( 0==pagerFlushOnCommit(pPager, 1) ){
|
||||
/* If this is an in-memory db, or no pages have been written to, or this
|
||||
** function has already been called, it is mostly a no-op. However, any
|
||||
** backup in progress needs to be restarted.
|
||||
*/
|
||||
** backup in progress needs to be restarted. */
|
||||
sqlite3BackupRestart(pPager->pBackup);
|
||||
}else{
|
||||
if( pagerUseWal(pPager) ){
|
||||
@@ -6539,10 +6553,10 @@ void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if this is an in-memory pager.
|
||||
** Return true if this is an in-memory or temp-file backed pager.
|
||||
*/
|
||||
int sqlite3PagerIsMemdb(Pager *pPager){
|
||||
return MEMDB;
|
||||
return pPager->tempFile;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6573,7 +6587,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
||||
pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
|
||||
);
|
||||
if( !aNew ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
|
||||
pPager->aSavepoint = aNew;
|
||||
@@ -6589,7 +6603,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
||||
aNew[ii].iSubRec = pPager->nSubRec;
|
||||
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
|
||||
if( !aNew[ii].pInSavepoint ){
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( pagerUseWal(pPager) ){
|
||||
sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
|
||||
@@ -6643,7 +6657,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
||||
** savepoint. If no errors occur, SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
int rc = pPager->errCode; /* Return code */
|
||||
int rc = pPager->errCode;
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
|
||||
#endif
|
||||
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
|
||||
@@ -6667,7 +6685,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
if( op==SAVEPOINT_RELEASE ){
|
||||
if( nNew==0 && isOpen(pPager->sjfd) ){
|
||||
/* Only truncate if it is an in-memory sub-journal. */
|
||||
if( sqlite3IsMemJournal(pPager->sjfd) ){
|
||||
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
|
||||
rc = sqlite3OsTruncate(pPager->sjfd, 0);
|
||||
assert( rc==SQLITE_OK );
|
||||
}
|
||||
@@ -6684,6 +6702,20 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
|
||||
assert(rc!=SQLITE_DONE);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
/* If the cache has been modified but the savepoint cannot be rolled
|
||||
** back journal_mode=off, put the pager in the error state. This way,
|
||||
** if the VFS used by this pager includes ZipVFS, the entire transaction
|
||||
** can be rolled back at the ZipVFS level. */
|
||||
else if(
|
||||
pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
&& pPager->eState>=PAGER_WRITER_CACHEMOD
|
||||
){
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -6738,14 +6770,6 @@ const char *sqlite3PagerJournalname(Pager *pPager){
|
||||
return pPager->zJournal;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if fsync() calls are disabled for this pager. Return FALSE
|
||||
** if fsync()s are executed normally.
|
||||
*/
|
||||
int sqlite3PagerNosync(Pager *pPager){
|
||||
return pPager->noSync;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
/*
|
||||
** Set or retrieve the codec for this pager
|
||||
@@ -6830,7 +6854,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
/* In order to be able to rollback, an in-memory database must journal
|
||||
** the page we are moving from.
|
||||
*/
|
||||
if( MEMDB ){
|
||||
assert( pPager->tempFile || !MEMDB );
|
||||
if( pPager->tempFile ){
|
||||
rc = sqlite3PagerWrite(pPg);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -6887,7 +6912,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
assert( !pPgOld || pPgOld->nRef==1 );
|
||||
if( pPgOld ){
|
||||
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
|
||||
if( MEMDB ){
|
||||
if( pPager->tempFile ){
|
||||
/* Do not discard pages from an in-memory database since we might
|
||||
** need to rollback later. Just move the page out of the way. */
|
||||
sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
|
||||
@@ -6904,8 +6929,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
** to exist, in case the transaction needs to roll back. Use pPgOld
|
||||
** as the original page since it has already been allocated.
|
||||
*/
|
||||
if( MEMDB ){
|
||||
assert( pPgOld );
|
||||
if( pPager->tempFile && pPgOld ){
|
||||
sqlite3PcacheMove(pPgOld, origPgno);
|
||||
sqlite3PagerUnrefNotNull(pPgOld);
|
||||
}
|
||||
@@ -7157,10 +7181,12 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
|
||||
assert( MEMDB==0 || pPager->tempFile );
|
||||
if( pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** This function is called when the user invokes "PRAGMA wal_checkpoint",
|
||||
@@ -7192,6 +7218,7 @@ int sqlite3PagerWalCallback(Pager *pPager){
|
||||
*/
|
||||
int sqlite3PagerWalSupported(Pager *pPager){
|
||||
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
|
||||
if( pPager->noLock ) return 0;
|
||||
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
|
||||
}
|
||||
|
||||
@@ -7335,6 +7362,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
pPager->pageSize, (u8*)pPager->pTmpSpace);
|
||||
pPager->pWal = 0;
|
||||
pagerFixMaplimit(pPager);
|
||||
if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -7384,7 +7412,6 @@ int sqlite3PagerWalFramesize(Pager *pPager){
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SQLITE_OMIT_DISKIO */
|
||||
|
||||
/* BEGIN SQLCIPHER */
|
||||
|
||||
+17
-6
@@ -14,8 +14,8 @@
|
||||
** at a time and provides a journal for rollback.
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
#define _PAGER_H_
|
||||
#ifndef SQLITE_PAGER_H
|
||||
#define SQLITE_PAGER_H
|
||||
|
||||
/*
|
||||
** Default maximum size for persistent journal files. A negative
|
||||
@@ -68,7 +68,11 @@ typedef struct PgHdr DbPage;
|
||||
#define PAGER_LOCKINGMODE_EXCLUSIVE 1
|
||||
|
||||
/*
|
||||
** Numeric constants that encode the journalmode.
|
||||
** Numeric constants that encode the journalmode.
|
||||
**
|
||||
** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY)
|
||||
** are exposed in the API via the "PRAGMA journal_mode" command and
|
||||
** therefore cannot be changed without a compatibility break.
|
||||
*/
|
||||
#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
|
||||
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
|
||||
@@ -86,6 +90,11 @@ typedef struct PgHdr DbPage;
|
||||
|
||||
/*
|
||||
** Flags for sqlite3PagerSetFlags()
|
||||
**
|
||||
** Value constraints (enforced via assert()):
|
||||
** PAGER_FULLFSYNC == SQLITE_FullFSync
|
||||
** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync
|
||||
** PAGER_CACHE_SPILL == SQLITE_CacheSpill
|
||||
*/
|
||||
#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
|
||||
#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
|
||||
@@ -169,10 +178,13 @@ int sqlite3PagerSharedLock(Pager *pPager);
|
||||
int sqlite3PagerWalCallback(Pager *pPager);
|
||||
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
|
||||
int sqlite3PagerCloseWal(Pager *pPager);
|
||||
int sqlite3PagerUseWal(Pager *pPager);
|
||||
# ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
|
||||
int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
||||
# endif
|
||||
#else
|
||||
# define sqlite3PagerUseWal(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
@@ -191,11 +203,10 @@ sqlite3_vfs *sqlite3PagerVfs(Pager*);
|
||||
sqlite3_file *sqlite3PagerFile(Pager*);
|
||||
sqlite3_file *sqlite3PagerJrnlFile(Pager*);
|
||||
const char *sqlite3PagerJournalname(Pager*);
|
||||
int sqlite3PagerNosync(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
void sqlite3PagerClearCache(Pager *);
|
||||
void sqlite3PagerClearCache(Pager*);
|
||||
int sqlite3SectorSize(sqlite3_file *);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
@@ -222,4 +233,4 @@ void *sqlite3PagerCodec(DbPage *);
|
||||
# define enable_simulated_io_errors()
|
||||
#endif
|
||||
|
||||
#endif /* _PAGER_H_ */
|
||||
#endif /* SQLITE_PAGER_H */
|
||||
|
||||
+303
-306
File diff suppressed because it is too large
Load Diff
+212
-30
@@ -14,7 +14,29 @@
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** A complete page cache is an instance of this structure.
|
||||
** A complete page cache is an instance of this structure. Every
|
||||
** entry in the cache holds a single page of the database file. The
|
||||
** btree layer only operates on the cached copy of the database pages.
|
||||
**
|
||||
** A page cache entry is "clean" if it exactly matches what is currently
|
||||
** on disk. A page is "dirty" if it has been modified and needs to be
|
||||
** persisted to disk.
|
||||
**
|
||||
** pDirty, pDirtyTail, pSynced:
|
||||
** All dirty pages are linked into the doubly linked list using
|
||||
** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order
|
||||
** such that p was added to the list more recently than p->pDirtyNext.
|
||||
** PCache.pDirty points to the first (newest) element in the list and
|
||||
** pDirtyTail to the last (oldest).
|
||||
**
|
||||
** The PCache.pSynced variable is used to optimize searching for a dirty
|
||||
** page to eject from the cache mid-transaction. It is better to eject
|
||||
** a page that does not require a journal sync than one that does.
|
||||
** Therefore, pSynced is maintained to that it *almost* always points
|
||||
** to either the oldest page in the pDirty/pDirtyTail list that has a
|
||||
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
|
||||
** (so that the right page to eject can be found by following pDirtyPrev
|
||||
** pointers).
|
||||
*/
|
||||
struct PCache {
|
||||
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
|
||||
@@ -31,6 +53,95 @@ struct PCache {
|
||||
sqlite3_pcache *pCache; /* Pluggable cache module */
|
||||
};
|
||||
|
||||
/********************************** Test and Debug Logic **********************/
|
||||
/*
|
||||
** Debug tracing macros. Enable by by changing the "0" to "1" and
|
||||
** recompiling.
|
||||
**
|
||||
** When sqlite3PcacheTrace is 1, single line trace messages are issued.
|
||||
** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries
|
||||
** is displayed for many operations, resulting in a lot of output.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) && 0
|
||||
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
|
||||
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
|
||||
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
|
||||
void pcacheDump(PCache *pCache){
|
||||
int N;
|
||||
int i, j;
|
||||
sqlite3_pcache_page *pLower;
|
||||
PgHdr *pPg;
|
||||
unsigned char *a;
|
||||
|
||||
if( sqlite3PcacheTrace<2 ) return;
|
||||
if( pCache->pCache==0 ) return;
|
||||
N = sqlite3PcachePagecount(pCache);
|
||||
if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
|
||||
for(i=1; i<=N; i++){
|
||||
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
|
||||
if( pLower==0 ) continue;
|
||||
pPg = (PgHdr*)pLower->pExtra;
|
||||
printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
|
||||
a = (unsigned char *)pLower->pBuf;
|
||||
for(j=0; j<12; j++) printf("%02x", a[j]);
|
||||
printf("\n");
|
||||
if( pPg->pPage==0 ){
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define pcacheTrace(X)
|
||||
# define pcacheDump(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Check invariants on a PgHdr entry. Return true if everything is OK.
|
||||
** Return false if any invariant is violated.
|
||||
**
|
||||
** This routine is for use inside of assert() statements only. For
|
||||
** example:
|
||||
**
|
||||
** assert( sqlite3PcachePageSanity(pPg) );
|
||||
*/
|
||||
#if SQLITE_DEBUG
|
||||
int sqlite3PcachePageSanity(PgHdr *pPg){
|
||||
PCache *pCache;
|
||||
assert( pPg!=0 );
|
||||
assert( pPg->pgno>0 ); /* Page number is 1 or more */
|
||||
pCache = pPg->pCache;
|
||||
assert( pCache!=0 ); /* Every page has an associated PCache */
|
||||
if( pPg->flags & PGHDR_CLEAN ){
|
||||
assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
|
||||
assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
|
||||
assert( pCache->pDirtyTail!=pPg );
|
||||
}
|
||||
/* WRITEABLE pages must also be DIRTY */
|
||||
if( pPg->flags & PGHDR_WRITEABLE ){
|
||||
assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
|
||||
}
|
||||
/* NEED_SYNC can be set independently of WRITEABLE. This can happen,
|
||||
** for example, when using the sqlite3PagerDontWrite() optimization:
|
||||
** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK.
|
||||
** (2) Page X moved to freelist, WRITEABLE is cleared
|
||||
** (3) Page X reused, WRITEABLE is set again
|
||||
** If NEED_SYNC had been cleared in step 2, then it would not be reset
|
||||
** in step 3, and page might be written into the database without first
|
||||
** syncing the rollback journal, which might cause corruption on a power
|
||||
** loss.
|
||||
**
|
||||
** Another example is when the database page size is smaller than the
|
||||
** disk sector size. When any page of a sector is journalled, all pages
|
||||
** in that sector are marked NEED_SYNC even if they are still CLEAN, just
|
||||
** in case they are later modified, since all pages in the same sector
|
||||
** must be journalled and synced before any of those pages can be safely
|
||||
** written.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
|
||||
/********************************** Linked List Management ********************/
|
||||
|
||||
/* Allowed values for second argument to pcacheManageDirtyList() */
|
||||
@@ -47,17 +158,16 @@ struct PCache {
|
||||
static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
PCache *p = pPage->pCache;
|
||||
|
||||
pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
|
||||
addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
|
||||
pPage->pgno));
|
||||
if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
|
||||
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
|
||||
assert( pPage->pDirtyPrev || pPage==p->pDirty );
|
||||
|
||||
/* Update the PCache1.pSynced variable if necessary. */
|
||||
if( p->pSynced==pPage ){
|
||||
PgHdr *pSynced = pPage->pDirtyPrev;
|
||||
while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
|
||||
pSynced = pSynced->pDirtyPrev;
|
||||
}
|
||||
p->pSynced = pSynced;
|
||||
p->pSynced = pPage->pDirtyPrev;
|
||||
}
|
||||
|
||||
if( pPage->pDirtyNext ){
|
||||
@@ -69,10 +179,15 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
if( pPage->pDirtyPrev ){
|
||||
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
|
||||
}else{
|
||||
/* If there are now no dirty pages in the cache, set eCreate to 2.
|
||||
** This is an optimization that allows sqlite3PcacheFetch() to skip
|
||||
** searching for a dirty page to eject from the cache when it might
|
||||
** otherwise have to. */
|
||||
assert( pPage==p->pDirty );
|
||||
p->pDirty = pPage->pDirtyNext;
|
||||
if( p->pDirty==0 && p->bPurgeable ){
|
||||
assert( p->eCreate==1 );
|
||||
assert( p->bPurgeable || p->eCreate==2 );
|
||||
if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
assert( p->bPurgeable==0 || p->eCreate==1 );
|
||||
p->eCreate = 2;
|
||||
}
|
||||
}
|
||||
@@ -94,10 +209,19 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
}
|
||||
}
|
||||
p->pDirty = pPage;
|
||||
if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
|
||||
|
||||
/* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
|
||||
** pSynced to point to it. Checking the NEED_SYNC flag is an
|
||||
** optimization, as if pSynced points to a page with the NEED_SYNC
|
||||
** flag set sqlite3PcacheFetchStress() searches through all newer
|
||||
** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
|
||||
if( !p->pSynced
|
||||
&& 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
|
||||
){
|
||||
p->pSynced = pPage;
|
||||
}
|
||||
}
|
||||
pcacheDump(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -106,7 +230,9 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
*/
|
||||
static void pcacheUnpin(PgHdr *p){
|
||||
if( p->pCache->bPurgeable ){
|
||||
pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
|
||||
pcacheDump(p->pCache);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +302,7 @@ int sqlite3PcacheOpen(
|
||||
p->pStress = pStress;
|
||||
p->szCache = 100;
|
||||
p->szSpill = 1;
|
||||
pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
|
||||
return sqlite3PcacheSetPageSize(p, szPage);
|
||||
}
|
||||
|
||||
@@ -191,13 +318,14 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
|
||||
pCache->bPurgeable
|
||||
);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
if( pNew==0 ) return SQLITE_NOMEM_BKPT;
|
||||
sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
||||
}
|
||||
pCache->pCache = pNew;
|
||||
pCache->szPage = szPage;
|
||||
pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -232,11 +360,13 @@ sqlite3_pcache_page *sqlite3PcacheFetch(
|
||||
int createFlag /* If true, create page if it does not exist already */
|
||||
){
|
||||
int eCreate;
|
||||
sqlite3_pcache_page *pRes;
|
||||
|
||||
assert( pCache!=0 );
|
||||
assert( pCache->pCache!=0 );
|
||||
assert( createFlag==3 || createFlag==0 );
|
||||
assert( pgno>0 );
|
||||
assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
|
||||
|
||||
/* eCreate defines what to do if the page does not exist.
|
||||
** 0 Do not allocate a new page. (createFlag==0)
|
||||
@@ -249,12 +379,15 @@ sqlite3_pcache_page *sqlite3PcacheFetch(
|
||||
assert( eCreate==0 || eCreate==1 || eCreate==2 );
|
||||
assert( createFlag==0 || pCache->eCreate==eCreate );
|
||||
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
|
||||
return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
|
||||
createFlag?" create":"",pRes));
|
||||
return pRes;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the sqlite3PcacheFetch() routine is unable to allocate a new
|
||||
** page because new clean pages are available for reuse and the cache
|
||||
** page because no clean pages are available for reuse and the cache
|
||||
** size limit has been reached, then this routine can be invoked to
|
||||
** try harder to allocate a page. This routine might invoke the stress
|
||||
** callback to spill dirty pages to the journal. It will then try to
|
||||
@@ -276,7 +409,11 @@ int sqlite3PcacheFetchStress(
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
*/
|
||||
**
|
||||
** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
|
||||
** flag is currently referenced, then the following may leave pSynced
|
||||
** set incorrectly (pointing to other than the LRU page with NEED_SYNC
|
||||
** cleared). This is Ok, as pSynced is just an optimization. */
|
||||
for(pPg=pCache->pSynced;
|
||||
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
||||
pPg=pPg->pDirtyPrev
|
||||
@@ -294,14 +431,16 @@ int sqlite3PcacheFetchStress(
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
numberOfCachePages(pCache));
|
||||
#endif
|
||||
pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
pcacheDump(pCache);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
*ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
|
||||
return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK;
|
||||
return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -322,7 +461,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
|
||||
assert( pPage!=0 );
|
||||
pPgHdr = (PgHdr*)pPage->pExtra;
|
||||
assert( pPgHdr->pPage==0 );
|
||||
memset(pPgHdr, 0, sizeof(PgHdr));
|
||||
memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
|
||||
pPgHdr->pPage = pPage;
|
||||
pPgHdr->pData = pPage->pBuf;
|
||||
pPgHdr->pExtra = (void *)&pPgHdr[1];
|
||||
@@ -354,6 +493,7 @@ PgHdr *sqlite3PcacheFetchFinish(
|
||||
}
|
||||
pCache->nRefSum++;
|
||||
pPgHdr->nRef++;
|
||||
assert( sqlite3PcachePageSanity(pPgHdr) );
|
||||
return pPgHdr;
|
||||
}
|
||||
|
||||
@@ -367,8 +507,11 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
||||
if( (--p->nRef)==0 ){
|
||||
if( p->flags&PGHDR_CLEAN ){
|
||||
pcacheUnpin(p);
|
||||
}else if( p->pDirtyPrev!=0 ){
|
||||
/* Move the page to the head of the dirty list. */
|
||||
}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. */
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
||||
}
|
||||
}
|
||||
@@ -379,6 +522,7 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheRef(PgHdr *p){
|
||||
assert(p->nRef>0);
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
p->nRef++;
|
||||
p->pCache->nRefSum++;
|
||||
}
|
||||
@@ -390,6 +534,7 @@ void sqlite3PcacheRef(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheDrop(PgHdr *p){
|
||||
assert( p->nRef==1 );
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->flags&PGHDR_DIRTY ){
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
||||
}
|
||||
@@ -403,13 +548,16 @@ void sqlite3PcacheDrop(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
assert( p->nRef>0 );
|
||||
if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
p->flags &= ~PGHDR_DONT_WRITE;
|
||||
if( p->flags & PGHDR_CLEAN ){
|
||||
p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
|
||||
pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
|
||||
assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
|
||||
}
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,11 +566,14 @@ void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
if( (p->flags & PGHDR_DIRTY) ){
|
||||
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);
|
||||
}
|
||||
@@ -434,11 +585,24 @@ void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheCleanAll(PCache *pCache){
|
||||
PgHdr *p;
|
||||
pcacheTrace(("%p.CLEAN-ALL\n",pCache));
|
||||
while( (p = pCache->pDirty)!=0 ){
|
||||
sqlite3PcacheMakeClean(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
|
||||
*/
|
||||
void sqlite3PcacheClearWritable(PCache *pCache){
|
||||
PgHdr *p;
|
||||
pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
|
||||
for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
||||
p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
||||
}
|
||||
pCache->pSynced = pCache->pDirtyTail;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
|
||||
*/
|
||||
@@ -457,6 +621,8 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
||||
PCache *pCache = p->pCache;
|
||||
assert( p->nRef>0 );
|
||||
assert( newPgno>0 );
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
|
||||
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
||||
p->pgno = newPgno;
|
||||
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
||||
@@ -477,6 +643,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
if( pCache->pCache ){
|
||||
PgHdr *p;
|
||||
PgHdr *pNext;
|
||||
pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
|
||||
for(p=pCache->pDirty; p; p=pNext){
|
||||
pNext = p->pDirtyNext;
|
||||
/* This routine never gets call with a positive pgno except right
|
||||
@@ -484,7 +651,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
** it must be that pgno==0.
|
||||
*/
|
||||
assert( p->pgno>0 );
|
||||
if( ALWAYS(p->pgno>pgno) ){
|
||||
if( p->pgno>pgno ){
|
||||
assert( p->flags&PGHDR_DIRTY );
|
||||
sqlite3PcacheMakeClean(p);
|
||||
}
|
||||
@@ -507,6 +674,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
*/
|
||||
void sqlite3PcacheClose(PCache *pCache){
|
||||
assert( pCache->pCache!=0 );
|
||||
pcacheTrace(("%p.CLOSE\n",pCache));
|
||||
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
||||
}
|
||||
|
||||
@@ -519,29 +687,31 @@ void sqlite3PcacheClear(PCache *pCache){
|
||||
|
||||
/*
|
||||
** Merge two lists of pages connected by pDirty and in pgno order.
|
||||
** Do not both fixing the pDirtyPrev pointers.
|
||||
** Do not bother fixing the pDirtyPrev pointers.
|
||||
*/
|
||||
static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
|
||||
PgHdr result, *pTail;
|
||||
pTail = &result;
|
||||
while( pA && pB ){
|
||||
assert( pA!=0 && pB!=0 );
|
||||
for(;;){
|
||||
if( pA->pgno<pB->pgno ){
|
||||
pTail->pDirty = pA;
|
||||
pTail = pA;
|
||||
pA = pA->pDirty;
|
||||
if( pA==0 ){
|
||||
pTail->pDirty = pB;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
pTail->pDirty = pB;
|
||||
pTail = pB;
|
||||
pB = pB->pDirty;
|
||||
if( pB==0 ){
|
||||
pTail->pDirty = pA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
pTail->pDirty = pA;
|
||||
}else if( pB ){
|
||||
pTail->pDirty = pB;
|
||||
}else{
|
||||
pTail->pDirty = 0;
|
||||
}
|
||||
return result.pDirty;
|
||||
}
|
||||
|
||||
@@ -582,7 +752,8 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
|
||||
}
|
||||
p = a[0];
|
||||
for(i=1; i<N_SORT_BUCKET; i++){
|
||||
p = pcacheMergeDirtyList(p, a[i]);
|
||||
if( a[i]==0 ) continue;
|
||||
p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -675,6 +846,17 @@ void sqlite3PcacheShrink(PCache *pCache){
|
||||
*/
|
||||
int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
|
||||
|
||||
/*
|
||||
** Return the number of dirty pages currently in the cache, as a percentage
|
||||
** of the configured cache size.
|
||||
*/
|
||||
int sqlite3PCachePercentDirty(PCache *pCache){
|
||||
PgHdr *pDirty;
|
||||
int nDirty = 0;
|
||||
int nCache = numberOfCachePages(pCache);
|
||||
for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;
|
||||
return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
|
||||
/*
|
||||
|
||||
+13
-5
@@ -26,7 +26,7 @@ struct PgHdr {
|
||||
sqlite3_pcache_page *pPage; /* Pcache object page handle */
|
||||
void *pData; /* Page data */
|
||||
void *pExtra; /* Extra content */
|
||||
PgHdr *pDirty; /* Transient list of dirty pages */
|
||||
PgHdr *pDirty; /* Transient list of dirty sorted by pgno */
|
||||
Pager *pPager; /* The pager this page is part of */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
@@ -51,11 +51,10 @@ struct PgHdr {
|
||||
#define PGHDR_WRITEABLE 0x004 /* Journaled and ready to modify */
|
||||
#define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before
|
||||
** writing this page to the database */
|
||||
#define PGHDR_NEED_READ 0x010 /* Content is unread */
|
||||
#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
|
||||
#define PGHDR_MMAP 0x040 /* This is an mmap page object */
|
||||
#define PGHDR_DONT_WRITE 0x010 /* Do not write content to disk */
|
||||
#define PGHDR_MMAP 0x020 /* This is an mmap page object */
|
||||
|
||||
#define PGHDR_WAL_APPEND 0x080 /* Appended to wal file */
|
||||
#define PGHDR_WAL_APPEND 0x040 /* Appended to wal file */
|
||||
|
||||
/* Initialize and shutdown the page cache subsystem */
|
||||
int sqlite3PcacheInitialize(void);
|
||||
@@ -99,6 +98,7 @@ void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */
|
||||
void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
|
||||
void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
|
||||
void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
|
||||
void sqlite3PcacheClearWritable(PCache*);
|
||||
|
||||
/* Change a page number. Used by incr-vacuum. */
|
||||
void sqlite3PcacheMove(PgHdr*, Pgno);
|
||||
@@ -137,6 +137,11 @@ int sqlite3PcachePagecount(PCache*);
|
||||
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
/* Check invariants on a PgHdr object */
|
||||
int sqlite3PcachePageSanity(PgHdr*);
|
||||
#endif
|
||||
|
||||
/* Set and get the suggested cache-size for the specified pager-cache.
|
||||
**
|
||||
** If no global maximum is configured, then the system attempts to limit
|
||||
@@ -173,4 +178,7 @@ void sqlite3PCacheSetDefault(void);
|
||||
int sqlite3HeaderSizePcache(void);
|
||||
int sqlite3HeaderSizePcache1(void);
|
||||
|
||||
/* Number of dirty pages as a percentage of the configured cache size */
|
||||
int sqlite3PCachePercentDirty(PCache*);
|
||||
|
||||
#endif /* _PCACHE_H_ */
|
||||
|
||||
+37
-15
@@ -279,7 +279,7 @@ static int pcache1InitBulk(PCache1 *pCache){
|
||||
szBulk = -1024 * (i64)pcache1.nInitPage;
|
||||
}
|
||||
if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
|
||||
szBulk = pCache->szAlloc*pCache->nMax;
|
||||
szBulk = pCache->szAlloc*(i64)pCache->nMax;
|
||||
}
|
||||
zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
|
||||
sqlite3EndBenignMalloc();
|
||||
@@ -348,7 +348,6 @@ static void *pcache1Alloc(int nByte){
|
||||
** Free an allocated buffer obtained from pcache1Alloc().
|
||||
*/
|
||||
static void pcache1Free(void *p){
|
||||
int nFreed = 0;
|
||||
if( p==0 ) return;
|
||||
if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
|
||||
PgFreeslot *pSlot;
|
||||
@@ -365,10 +364,13 @@ static void pcache1Free(void *p){
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
|
||||
nFreed = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
{
|
||||
int nFreed = 0;
|
||||
nFreed = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}
|
||||
#endif
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -630,12 +632,30 @@ static void pcache1TruncateUnsafe(
|
||||
PCache1 *pCache, /* The cache to truncate */
|
||||
unsigned int iLimit /* Drop pages with this pgno or larger */
|
||||
){
|
||||
TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */
|
||||
unsigned int h;
|
||||
TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */
|
||||
unsigned int h, iStop;
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
for(h=0; h<pCache->nHash; h++){
|
||||
PgHdr1 **pp = &pCache->apHash[h];
|
||||
assert( pCache->iMaxKey >= iLimit );
|
||||
assert( pCache->nHash > 0 );
|
||||
if( pCache->iMaxKey - iLimit < pCache->nHash ){
|
||||
/* If we are just shaving the last few pages off the end of the
|
||||
** cache, then there is no point in scanning the entire hash table.
|
||||
** Only scan those hash slots that might contain pages that need to
|
||||
** be removed. */
|
||||
h = iLimit % pCache->nHash;
|
||||
iStop = pCache->iMaxKey % pCache->nHash;
|
||||
TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */
|
||||
}else{
|
||||
/* This is the general case where many pages are being removed.
|
||||
** It is necessary to scan the entire hash table */
|
||||
h = pCache->nHash/2;
|
||||
iStop = h - 1;
|
||||
}
|
||||
for(;;){
|
||||
PgHdr1 **pp;
|
||||
PgHdr1 *pPage;
|
||||
assert( h<pCache->nHash );
|
||||
pp = &pCache->apHash[h];
|
||||
while( (pPage = *pp)!=0 ){
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pCache->nPage--;
|
||||
@@ -644,11 +664,13 @@ static void pcache1TruncateUnsafe(
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
TESTONLY( nPage++; )
|
||||
TESTONLY( if( nPage>=0 ) nPage++; )
|
||||
}
|
||||
}
|
||||
if( h==iStop ) break;
|
||||
h = (h+1) % pCache->nHash;
|
||||
}
|
||||
assert( pCache->nPage==nPage );
|
||||
assert( nPage<0 || pCache->nPage==(unsigned)nPage );
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -688,8 +710,8 @@ static int pcache1Init(void *NotUsed){
|
||||
|
||||
#if SQLITE_THREADSAFE
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
|
||||
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
|
||||
pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
|
||||
pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM);
|
||||
}
|
||||
#endif
|
||||
if( pcache1.separateCache
|
||||
@@ -1125,7 +1147,7 @@ static void pcache1Destroy(sqlite3_pcache *p){
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
|
||||
pcache1EnterMutex(pGroup);
|
||||
pcache1TruncateUnsafe(pCache, 0);
|
||||
if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0);
|
||||
assert( pGroup->nMaxPage >= pCache->nMax );
|
||||
pGroup->nMaxPage -= pCache->nMax;
|
||||
assert( pGroup->nMinPage >= pCache->nMin );
|
||||
|
||||
+44
-32
@@ -343,7 +343,7 @@ void sqlite3Pragma(
|
||||
}
|
||||
|
||||
assert( pId2 );
|
||||
zDb = pId2->n>0 ? pDb->zName : 0;
|
||||
zDb = pId2->n>0 ? pDb->zDbSName : 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
@@ -1008,6 +1008,7 @@ void sqlite3Pragma(
|
||||
int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK;
|
||||
if( iLevel==0 ) iLevel = 1;
|
||||
pDb->safety_level = iLevel;
|
||||
pDb->bSyncSet = 1;
|
||||
setAllPagerFlags(db);
|
||||
}
|
||||
}
|
||||
@@ -1044,7 +1045,7 @@ void sqlite3Pragma(
|
||||
** compiler (eg. count_changes). So add an opcode to expire all
|
||||
** compiled SQL statements after modifying a pragma value.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
setAllPagerFlags(db);
|
||||
}
|
||||
break;
|
||||
@@ -1066,7 +1067,7 @@ void sqlite3Pragma(
|
||||
*/
|
||||
case PragTyp_TABLE_INFO: if( zRight ){
|
||||
Table *pTab;
|
||||
pTab = sqlite3FindTable(db, zRight, zDb);
|
||||
pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
|
||||
if( pTab ){
|
||||
static const char *azCol[] = {
|
||||
"cid", "name", "type", "notnull", "dflt_value", "pk"
|
||||
@@ -1091,12 +1092,13 @@ void sqlite3Pragma(
|
||||
}else{
|
||||
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
|
||||
}
|
||||
assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
|
||||
sqlite3VdbeMultiLoad(v, 1, "issisi",
|
||||
i-nHidden,
|
||||
pCol->zName,
|
||||
pCol->zType ? pCol->zType : "",
|
||||
sqlite3ColumnType(pCol,""),
|
||||
pCol->notNull ? 1 : 0,
|
||||
pCol->zDflt,
|
||||
pCol->pDflt ? pCol->pDflt->u.zToken : 0,
|
||||
k);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
|
||||
}
|
||||
@@ -1117,14 +1119,14 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeMultiLoad(v, 1, "ssii",
|
||||
pTab->zName,
|
||||
0,
|
||||
(int)sqlite3LogEstToInt(pTab->szTabRow),
|
||||
(int)sqlite3LogEstToInt(pTab->nRowLogEst));
|
||||
pTab->szTabRow,
|
||||
pTab->nRowLogEst);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqlite3VdbeMultiLoad(v, 2, "sii",
|
||||
pIdx->zName,
|
||||
(int)sqlite3LogEstToInt(pIdx->szIdxRow),
|
||||
(int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]));
|
||||
pIdx->szIdxRow,
|
||||
pIdx->aiRowLogEst[0]);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
}
|
||||
}
|
||||
@@ -1204,10 +1206,10 @@ void sqlite3Pragma(
|
||||
setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ) continue;
|
||||
assert( db->aDb[i].zName!=0 );
|
||||
assert( db->aDb[i].zDbSName!=0 );
|
||||
sqlite3VdbeMultiLoad(v, 1, "iss",
|
||||
i,
|
||||
db->aDb[i].zName,
|
||||
db->aDb[i].zDbSName,
|
||||
sqlite3BtreeGetFilename(db->aDb[i].pBt));
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
|
||||
}
|
||||
@@ -1347,12 +1349,10 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
|
||||
sqlite3ColumnDefault(v, pTab, iKey, regRow);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
|
||||
sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, i, 0, regRow); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, addrOk);
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||
}else{
|
||||
@@ -1450,7 +1450,10 @@ void sqlite3Pragma(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
HashElem *x;
|
||||
Hash *pTbls;
|
||||
int *aRoot;
|
||||
int cnt = 0;
|
||||
int mxIdx = 0;
|
||||
int nIdx;
|
||||
|
||||
if( OMIT_TEMPDB && i==1 ) continue;
|
||||
if( iDb>=0 && i!=iDb ) continue;
|
||||
@@ -1463,35 +1466,39 @@ void sqlite3Pragma(
|
||||
|
||||
/* Do an integrity check of the B-Tree
|
||||
**
|
||||
** Begin by filling registers 2, 3, ... with the root pages numbers
|
||||
** Begin by finding the root pages numbers
|
||||
** for all tables and indices in the database.
|
||||
*/
|
||||
assert( sqlite3SchemaMutexHeld(db, i, 0) );
|
||||
pTbls = &db->aDb[i].pSchema->tblHash;
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
cnt++;
|
||||
}
|
||||
if( HasRowid(pTab) ) cnt++;
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
|
||||
if( nIdx>mxIdx ) mxIdx = nIdx;
|
||||
}
|
||||
aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
|
||||
if( aRoot==0 ) break;
|
||||
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
if( HasRowid(pTab) ) aRoot[cnt++] = pTab->tnum;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
cnt++;
|
||||
aRoot[cnt++] = pIdx->tnum;
|
||||
}
|
||||
}
|
||||
aRoot[cnt] = 0;
|
||||
|
||||
/* Make sure sufficient number of registers have been allocated */
|
||||
pParse->nMem = MAX( pParse->nMem, cnt+8 );
|
||||
pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
|
||||
|
||||
/* Do the b-tree integrity checks */
|
||||
sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
|
||||
sqlite3VdbeChangeP5(v, (u8)i);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
|
||||
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);
|
||||
@@ -1521,7 +1528,8 @@ void sqlite3Pragma(
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
|
||||
}
|
||||
pParse->nMem = MAX(pParse->nMem, 8+j);
|
||||
assert( pParse->nMem>=8+j );
|
||||
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
|
||||
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
|
||||
/* Verify that all NOT NULL columns really are NOT NULL */
|
||||
@@ -1713,7 +1721,9 @@ void sqlite3Pragma(
|
||||
** PRAGMA [schema.]user_version
|
||||
** PRAGMA [schema.]user_version = <integer>
|
||||
**
|
||||
** PRAGMA [schema.]freelist_count = <integer>
|
||||
** PRAGMA [schema.]freelist_count
|
||||
**
|
||||
** PRAGMA [schema.]data_version
|
||||
**
|
||||
** PRAGMA [schema.]application_id
|
||||
** PRAGMA [schema.]application_id = <integer>
|
||||
@@ -1769,6 +1779,7 @@ void sqlite3Pragma(
|
||||
aOp[1].p3 = iCookie;
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
|
||||
sqlite3VdbeReusable(v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1790,6 +1801,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeLoadString(v, 1, zOpt);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
sqlite3VdbeReusable(v);
|
||||
}
|
||||
break;
|
||||
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
@@ -1925,15 +1937,15 @@ void sqlite3Pragma(
|
||||
Btree *pBt;
|
||||
const char *zState = "unknown";
|
||||
int j;
|
||||
if( db->aDb[i].zName==0 ) continue;
|
||||
if( db->aDb[i].zDbSName==0 ) continue;
|
||||
pBt = db->aDb[i].pBt;
|
||||
if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
|
||||
zState = "closed";
|
||||
}else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0,
|
||||
}else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
|
||||
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
|
||||
zState = azLockName[j];
|
||||
}
|
||||
sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zName, zState);
|
||||
sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zDbSName, zState);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
+38
-42
@@ -33,7 +33,7 @@ static void corruptSchema(
|
||||
sqlite3DbFree(db, *pData->pzErrMsg);
|
||||
*pData->pzErrMsg = z;
|
||||
}
|
||||
pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
|
||||
pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -73,6 +73,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** structures that describe the table, index, or view.
|
||||
*/
|
||||
int rc;
|
||||
u8 saved_iDb = db->init.iDb;
|
||||
sqlite3_stmt *pStmt;
|
||||
TESTONLY(int rcp); /* Return code from sqlite3_prepare() */
|
||||
|
||||
@@ -83,7 +84,8 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = 0;
|
||||
db->init.iDb = saved_iDb;
|
||||
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
@@ -107,7 +109,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** to do here is record the root page number for that index.
|
||||
*/
|
||||
Index *pIndex;
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
|
||||
if( pIndex==0 ){
|
||||
/* This can occur if there exists an index on a TEMP table which
|
||||
** has the same name as another index on a permanent index. Since
|
||||
@@ -286,7 +288,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
|
||||
db->aDb[iDb].zName, zMasterName);
|
||||
db->aDb[iDb].zDbSName, zMasterName);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
sqlite3_xauth xAuth;
|
||||
@@ -307,7 +309,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
#endif
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
|
||||
@@ -516,18 +518,14 @@ static int sqlite3Prepare(
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
Parse *pParse; /* Parsing context */
|
||||
char *zErrMsg = 0; /* Error message */
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
int i; /* Loop counter */
|
||||
Parse sParse; /* Parsing context */
|
||||
|
||||
/* Allocate the parsing context */
|
||||
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto end_prepare;
|
||||
}
|
||||
pParse->pReprepare = pReprepare;
|
||||
memset(&sParse, 0, PARSE_HDR_SZ);
|
||||
memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
|
||||
sParse.pReprepare = pReprepare;
|
||||
assert( ppStmt && *ppStmt==0 );
|
||||
/* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
@@ -561,7 +559,7 @@ static int sqlite3Prepare(
|
||||
assert( sqlite3BtreeHoldsMutex(pBt) );
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
const char *zDb = db->aDb[i].zDbSName;
|
||||
sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
|
||||
testcase( db->flags & SQLITE_ReadUncommitted );
|
||||
goto end_prepare;
|
||||
@@ -571,8 +569,7 @@ static int sqlite3Prepare(
|
||||
|
||||
sqlite3VtabUnlockList(db);
|
||||
|
||||
pParse->db = db;
|
||||
pParse->nQueryLoop = 0; /* Logarithmic, so 0 really means 1 */
|
||||
sParse.db = db;
|
||||
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
|
||||
char *zSqlCopy;
|
||||
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
@@ -585,61 +582,61 @@ static int sqlite3Prepare(
|
||||
}
|
||||
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
|
||||
if( zSqlCopy ){
|
||||
sqlite3RunParser(pParse, zSqlCopy, &zErrMsg);
|
||||
pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
|
||||
sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
|
||||
sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
|
||||
sqlite3DbFree(db, zSqlCopy);
|
||||
}else{
|
||||
pParse->zTail = &zSql[nBytes];
|
||||
sParse.zTail = &zSql[nBytes];
|
||||
}
|
||||
}else{
|
||||
sqlite3RunParser(pParse, zSql, &zErrMsg);
|
||||
sqlite3RunParser(&sParse, zSql, &zErrMsg);
|
||||
}
|
||||
assert( 0==pParse->nQueryLoop );
|
||||
assert( 0==sParse.nQueryLoop );
|
||||
|
||||
if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK;
|
||||
if( pParse->checkSchema ){
|
||||
schemaIsValid(pParse);
|
||||
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
|
||||
if( sParse.checkSchema ){
|
||||
schemaIsValid(&sParse);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
sParse.rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( pzTail ){
|
||||
*pzTail = pParse->zTail;
|
||||
*pzTail = sParse.zTail;
|
||||
}
|
||||
rc = pParse->rc;
|
||||
rc = sParse.rc;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){
|
||||
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"
|
||||
};
|
||||
int iFirst, mx;
|
||||
if( pParse->explain==2 ){
|
||||
sqlite3VdbeSetNumCols(pParse->pVdbe, 4);
|
||||
if( sParse.explain==2 ){
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 4);
|
||||
iFirst = 8;
|
||||
mx = 12;
|
||||
}else{
|
||||
sqlite3VdbeSetNumCols(pParse->pVdbe, 8);
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
|
||||
iFirst = 0;
|
||||
mx = 8;
|
||||
}
|
||||
for(i=iFirst; i<mx; i++){
|
||||
sqlite3VdbeSetColName(pParse->pVdbe, i-iFirst, COLNAME_NAME,
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME,
|
||||
azColName[i], SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( db->init.busy==0 ){
|
||||
Vdbe *pVdbe = pParse->pVdbe;
|
||||
sqlite3VdbeSetSql(pVdbe, zSql, (int)(pParse->zTail-zSql), saveSqlFlag);
|
||||
Vdbe *pVdbe = sParse.pVdbe;
|
||||
sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
|
||||
}
|
||||
if( pParse->pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(pParse->pVdbe);
|
||||
if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
assert(!(*ppStmt));
|
||||
}else{
|
||||
*ppStmt = (sqlite3_stmt*)pParse->pVdbe;
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
}
|
||||
|
||||
if( zErrMsg ){
|
||||
@@ -650,16 +647,15 @@ static int sqlite3Prepare(
|
||||
}
|
||||
|
||||
/* Delete any TriggerPrg structures allocated while parsing this statement. */
|
||||
while( pParse->pTriggerPrg ){
|
||||
TriggerPrg *pT = pParse->pTriggerPrg;
|
||||
pParse->pTriggerPrg = pT->pNext;
|
||||
while( sParse.pTriggerPrg ){
|
||||
TriggerPrg *pT = sParse.pTriggerPrg;
|
||||
sParse.pTriggerPrg = pT->pNext;
|
||||
sqlite3DbFree(db, pT);
|
||||
}
|
||||
|
||||
end_prepare:
|
||||
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
sqlite3ParserReset(&sParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
return rc;
|
||||
|
||||
+18
-18
@@ -15,26 +15,26 @@
|
||||
** Conversion types fall into various categories as defined by the
|
||||
** following enumeration.
|
||||
*/
|
||||
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 2 /* Floating point. %f */
|
||||
#define etEXP 3 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 5 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 6 /* Strings. %s */
|
||||
#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 8 /* Percent symbol. %% */
|
||||
#define etCHARX 9 /* Characters. %c */
|
||||
#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 1 /* Floating point. %f */
|
||||
#define etEXP 2 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 4 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 5 /* Strings. %s */
|
||||
#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 7 /* Percent symbol. %% */
|
||||
#define etCHARX 8 /* Characters. %c */
|
||||
/* The rest are extensions, not normally found in printf() */
|
||||
#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '',
|
||||
#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
|
||||
NULL pointers replaced by SQL NULL. %Q */
|
||||
#define etTOKEN 12 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 13 /* a pointer to a SrcList */
|
||||
#define etPOINTER 14 /* The %p conversion */
|
||||
#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
|
||||
#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
||||
#define etTOKEN 11 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 12 /* a pointer to a SrcList */
|
||||
#define etPOINTER 13 /* The %p conversion */
|
||||
#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
|
||||
#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
||||
|
||||
#define etINVALID 0 /* Any unrecognized conversion type */
|
||||
#define etINVALID 16 /* Any unrecognized conversion type */
|
||||
|
||||
|
||||
/*
|
||||
@@ -189,7 +189,7 @@ void sqlite3VXPrintf(
|
||||
etByte flag_long; /* True if "l" flag is present */
|
||||
etByte flag_longlong; /* True if the "ll" flag is present */
|
||||
etByte done; /* Loop termination flag */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
etByte xtype = etINVALID; /* Conversion paradigm */
|
||||
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
|
||||
u8 useIntern; /* Ok to use internal conversions (ex: %T) */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
|
||||
+62
-17
@@ -221,8 +221,8 @@ static int lookupName(
|
||||
zDb = 0;
|
||||
}else{
|
||||
for(i=0; i<db->nDb; i++){
|
||||
assert( db->aDb[i].zName );
|
||||
if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
|
||||
assert( db->aDb[i].zDbSName );
|
||||
if( sqlite3StrICmp(db->aDb[i].zDbSName,zDb)==0 ){
|
||||
pSchema = db->aDb[i].pSchema;
|
||||
break;
|
||||
}
|
||||
@@ -400,6 +400,10 @@ static int lookupName(
|
||||
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
|
||||
return WRC_Abort;
|
||||
}
|
||||
if( sqlite3ExprVectorSize(pOrig)!=1 ){
|
||||
sqlite3ErrorMsg(pParse, "row value misused");
|
||||
return WRC_Abort;
|
||||
}
|
||||
resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
|
||||
cnt = 1;
|
||||
pMatch = 0;
|
||||
@@ -623,7 +627,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
|
||||
/* if( pSrcList==0 ) break; */
|
||||
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
|
||||
/*notValid(pParse, pNC, "the \".\" operator", NC_PartIdx|NC_IsCheck, 1);*/
|
||||
pRight = pExpr->pRight;
|
||||
if( pRight->op==TK_ID ){
|
||||
zDb = 0;
|
||||
@@ -646,19 +649,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
int no_such_func = 0; /* True if no such function exists */
|
||||
int wrong_num_args = 0; /* True if wrong number of arguments */
|
||||
int is_agg = 0; /* True if is an aggregate function */
|
||||
int auth; /* Authorization to use the function */
|
||||
int nId; /* Number of characters in function name */
|
||||
const char *zId; /* The function name. */
|
||||
FuncDef *pDef; /* Information about the function */
|
||||
u8 enc = ENC(pParse->db); /* The database encoding */
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
notValid(pParse, pNC, "functions", NC_PartIdx);
|
||||
zId = pExpr->u.zToken;
|
||||
nId = sqlite3Strlen30(zId);
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
|
||||
if( pDef==0 ){
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, nId, -2, enc, 0);
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0);
|
||||
if( pDef==0 ){
|
||||
no_such_func = 1;
|
||||
}else{
|
||||
@@ -690,15 +691,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
|
||||
if( auth!=SQLITE_OK ){
|
||||
if( auth==SQLITE_DENY ){
|
||||
sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
|
||||
pDef->zName);
|
||||
pNC->nErr++;
|
||||
{
|
||||
int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0);
|
||||
if( auth!=SQLITE_OK ){
|
||||
if( auth==SQLITE_DENY ){
|
||||
sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
|
||||
pDef->zName);
|
||||
pNC->nErr++;
|
||||
}
|
||||
pExpr->op = TK_NULL;
|
||||
return WRC_Prune;
|
||||
}
|
||||
pExpr->op = TK_NULL;
|
||||
return WRC_Prune;
|
||||
}
|
||||
#endif
|
||||
if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
|
||||
@@ -711,14 +714,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
/* Date/time functions that use 'now', and other functions like
|
||||
** sqlite_version() that might change over time cannot be used
|
||||
** in an index. */
|
||||
notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr);
|
||||
notValid(pParse, pNC, "non-deterministic functions",
|
||||
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 ){
|
||||
}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 ){
|
||||
@@ -763,6 +771,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
assert( pNC->nRef>=nRef );
|
||||
if( nRef!=pNC->nRef ){
|
||||
ExprSetProperty(pExpr, EP_VarSelect);
|
||||
pNC->ncFlags |= NC_VarSelect;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -771,6 +780,42 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
int nLeft, nRight;
|
||||
if( pParse->db->mallocFailed ) break;
|
||||
assert( pExpr->pLeft!=0 );
|
||||
nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||
if( pExpr->op==TK_BETWEEN ){
|
||||
nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr);
|
||||
if( nRight==nLeft ){
|
||||
nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr);
|
||||
}
|
||||
}else{
|
||||
assert( pExpr->pRight!=0 );
|
||||
nRight = sqlite3ExprVectorSize(pExpr->pRight);
|
||||
}
|
||||
if( nLeft!=nRight ){
|
||||
testcase( pExpr->op==TK_EQ );
|
||||
testcase( pExpr->op==TK_NE );
|
||||
testcase( pExpr->op==TK_LT );
|
||||
testcase( pExpr->op==TK_LE );
|
||||
testcase( pExpr->op==TK_GT );
|
||||
testcase( pExpr->op==TK_GE );
|
||||
testcase( pExpr->op==TK_IS );
|
||||
testcase( pExpr->op==TK_ISNOT );
|
||||
testcase( pExpr->op==TK_BETWEEN );
|
||||
sqlite3ErrorMsg(pParse, "row value misused");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
|
||||
}
|
||||
|
||||
+65
-75
@@ -57,8 +57,9 @@
|
||||
** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
|
||||
** primitives are constant time. The cost of DESTROY is O(N).
|
||||
**
|
||||
** There is an added cost of O(N) when switching between TEST and
|
||||
** SMALLEST primitives.
|
||||
** TEST and SMALLEST may not be used by the same RowSet. This used to
|
||||
** be possible, but the feature was not used, so it was removed in order
|
||||
** to simplify the code.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -179,7 +180,9 @@ void sqlite3RowSetClear(RowSet *p){
|
||||
*/
|
||||
static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
|
||||
assert( p!=0 );
|
||||
if( p->nFresh==0 ){
|
||||
if( p->nFresh==0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* We could allocate a fresh RowSetEntry each time one is needed, but it
|
||||
** is more efficient to pull a preallocated entry from the pool */
|
||||
struct RowSetChunk *pNew;
|
||||
pNew = sqlite3DbMallocRawNN(p->db, sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
@@ -213,7 +216,9 @@ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
pEntry->pRight = 0;
|
||||
pLast = p->pLast;
|
||||
if( pLast ){
|
||||
if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){
|
||||
if( rowid<=pLast->v ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* Avoid unnecessary sorts by preserving the ROWSET_SORTED flags
|
||||
** where possible */
|
||||
p->rsFlags &= ~ROWSET_SORTED;
|
||||
}
|
||||
pLast->pRight = pEntry;
|
||||
@@ -237,28 +242,26 @@ static struct RowSetEntry *rowSetEntryMerge(
|
||||
struct RowSetEntry *pTail;
|
||||
|
||||
pTail = &head;
|
||||
while( pA && pB ){
|
||||
assert( pA!=0 && pB!=0 );
|
||||
for(;;){
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
assert( pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
if( pA->v<pB->v ){
|
||||
pTail->pRight = pA;
|
||||
if( pA->v<=pB->v ){
|
||||
if( pA->v<pB->v ) pTail = pTail->pRight = pA;
|
||||
pA = pA->pRight;
|
||||
pTail = pTail->pRight;
|
||||
}else if( pB->v<pA->v ){
|
||||
pTail->pRight = pB;
|
||||
pB = pB->pRight;
|
||||
pTail = pTail->pRight;
|
||||
if( pA==0 ){
|
||||
pTail->pRight = pB;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
pA = pA->pRight;
|
||||
pTail = pTail->pRight = pB;
|
||||
pB = pB->pRight;
|
||||
if( pB==0 ){
|
||||
pTail->pRight = pA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
pTail->pRight = pA;
|
||||
}else{
|
||||
assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
pTail->pRight = pB;
|
||||
}
|
||||
return head.pRight;
|
||||
}
|
||||
|
||||
@@ -281,9 +284,10 @@ static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
|
||||
aBucket[i] = pIn;
|
||||
pIn = pNext;
|
||||
}
|
||||
pIn = 0;
|
||||
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
pIn = rowSetEntryMerge(pIn, aBucket[i]);
|
||||
pIn = aBucket[0];
|
||||
for(i=1; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
if( aBucket[i]==0 ) continue;
|
||||
pIn = pIn ? rowSetEntryMerge(pIn, aBucket[i]) : aBucket[i];
|
||||
}
|
||||
return pIn;
|
||||
}
|
||||
@@ -335,23 +339,29 @@ static struct RowSetEntry *rowSetNDeepTree(
|
||||
){
|
||||
struct RowSetEntry *p; /* Root of the new tree */
|
||||
struct RowSetEntry *pLeft; /* Left subtree */
|
||||
if( *ppList==0 ){
|
||||
return 0;
|
||||
if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
/* Prevent unnecessary deep recursion when we run out of entries */
|
||||
return 0;
|
||||
}
|
||||
if( iDepth==1 ){
|
||||
if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
/* This branch causes a *balanced* tree to be generated. A valid tree
|
||||
** is still generated without this branch, but the tree is wildly
|
||||
** unbalanced and inefficient. */
|
||||
pLeft = rowSetNDeepTree(ppList, iDepth-1);
|
||||
p = *ppList;
|
||||
if( p==0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* It is safe to always return here, but the resulting tree
|
||||
** would be unbalanced */
|
||||
return pLeft;
|
||||
}
|
||||
p->pLeft = pLeft;
|
||||
*ppList = p->pRight;
|
||||
p->pRight = rowSetNDeepTree(ppList, iDepth-1);
|
||||
}else{
|
||||
p = *ppList;
|
||||
*ppList = p->pRight;
|
||||
p->pLeft = p->pRight = 0;
|
||||
return p;
|
||||
}
|
||||
pLeft = rowSetNDeepTree(ppList, iDepth-1);
|
||||
p = *ppList;
|
||||
if( p==0 ){
|
||||
return pLeft;
|
||||
}
|
||||
p->pLeft = pLeft;
|
||||
*ppList = p->pRight;
|
||||
p->pRight = rowSetNDeepTree(ppList, iDepth-1);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -378,59 +388,37 @@ static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Take all the entries on p->pEntry and on the trees in p->pForest and
|
||||
** sort them all together into one big ordered list on p->pEntry.
|
||||
**
|
||||
** This routine should only be called once in the life of a RowSet.
|
||||
*/
|
||||
static void rowSetToList(RowSet *p){
|
||||
|
||||
/* This routine is called only once */
|
||||
assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
|
||||
|
||||
if( (p->rsFlags & ROWSET_SORTED)==0 ){
|
||||
p->pEntry = rowSetEntrySort(p->pEntry);
|
||||
}
|
||||
|
||||
/* While this module could theoretically support it, sqlite3RowSetNext()
|
||||
** is never called after sqlite3RowSetText() for the same RowSet. So
|
||||
** there is never a forest to deal with. Should this change, simply
|
||||
** remove the assert() and the #if 0. */
|
||||
assert( p->pForest==0 );
|
||||
#if 0
|
||||
while( p->pForest ){
|
||||
struct RowSetEntry *pTree = p->pForest->pLeft;
|
||||
if( pTree ){
|
||||
struct RowSetEntry *pHead, *pTail;
|
||||
rowSetTreeToList(pTree, &pHead, &pTail);
|
||||
p->pEntry = rowSetEntryMerge(p->pEntry, pHead);
|
||||
}
|
||||
p->pForest = p->pForest->pRight;
|
||||
}
|
||||
#endif
|
||||
p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the smallest element from the RowSet.
|
||||
** Write the element into *pRowid. Return 1 on success. Return
|
||||
** 0 if the RowSet is already empty.
|
||||
**
|
||||
** After this routine has been called, the sqlite3RowSetInsert()
|
||||
** routine may not be called again.
|
||||
** routine may not be called again.
|
||||
**
|
||||
** This routine may not be called after sqlite3RowSetTest() has
|
||||
** been used. Older versions of RowSet allowed that, but as the
|
||||
** capability was not used by the code generator, it was removed
|
||||
** for code economy.
|
||||
*/
|
||||
int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
|
||||
assert( p!=0 );
|
||||
assert( p->pForest==0 ); /* Cannot be used with sqlite3RowSetText() */
|
||||
|
||||
/* Merge the forest into a single sorted list on first call */
|
||||
if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p);
|
||||
if( (p->rsFlags & ROWSET_NEXT)==0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
if( (p->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
p->pEntry = rowSetEntrySort(p->pEntry);
|
||||
}
|
||||
p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT;
|
||||
}
|
||||
|
||||
/* Return the next entry on the list */
|
||||
if( p->pEntry ){
|
||||
*pRowid = p->pEntry->v;
|
||||
p->pEntry = p->pEntry->pRight;
|
||||
if( p->pEntry==0 ){
|
||||
if( p->pEntry==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
/* Free memory immediately, rather than waiting on sqlite3_finalize() */
|
||||
sqlite3RowSetClear(p);
|
||||
}
|
||||
return 1;
|
||||
@@ -453,13 +441,15 @@ int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
|
||||
/* This routine is never called after sqlite3RowSetNext() */
|
||||
assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
|
||||
|
||||
/* Sort entries into the forest on the first test of a new batch
|
||||
/* Sort entries into the forest on the first test of a new batch.
|
||||
** To save unnecessary work, only do this when the batch number changes.
|
||||
*/
|
||||
if( iBatch!=pRowSet->iBatch ){
|
||||
if( iBatch!=pRowSet->iBatch ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
p = pRowSet->pEntry;
|
||||
if( p ){
|
||||
struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
|
||||
if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){
|
||||
if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* Only sort the current set of entiries if they need it */
|
||||
p = rowSetEntrySort(p);
|
||||
}
|
||||
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
|
||||
|
||||
+149
-119
@@ -56,6 +56,7 @@ struct SortCtx {
|
||||
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
|
||||
int labelDone; /* Jump here when done, ex: LIMIT reached */
|
||||
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
|
||||
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
|
||||
};
|
||||
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
|
||||
|
||||
@@ -74,7 +75,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
||||
sqlite3ExprListDelete(db, p->pOrderBy);
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
sqlite3ExprDelete(db, p->pOffset);
|
||||
sqlite3WithDelete(db, p->pWith);
|
||||
if( p->pWith ) sqlite3WithDelete(db, p->pWith);
|
||||
if( bFree ) sqlite3DbFree(db, p);
|
||||
p = pPrior;
|
||||
bFree = 1;
|
||||
@@ -87,7 +88,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
||||
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
|
||||
pDest->eDest = (u8)eDest;
|
||||
pDest->iSDParm = iParm;
|
||||
pDest->affSdst = 0;
|
||||
pDest->zAffSdst = 0;
|
||||
pDest->iSdst = 0;
|
||||
pDest->nSdst = 0;
|
||||
}
|
||||
@@ -105,7 +106,7 @@ Select *sqlite3SelectNew(
|
||||
ExprList *pGroupBy, /* the GROUP BY clause */
|
||||
Expr *pHaving, /* the HAVING clause */
|
||||
ExprList *pOrderBy, /* the ORDER BY clause */
|
||||
u16 selFlags, /* Flag parameters, such as SF_Distinct */
|
||||
u32 selFlags, /* Flag parameters, such as SF_Distinct */
|
||||
Expr *pLimit, /* LIMIT value. NULL means not used */
|
||||
Expr *pOffset /* OFFSET value. NULL means no offset */
|
||||
){
|
||||
@@ -169,7 +170,7 @@ void sqlite3SelectSetName(Select *p, const char *zName){
|
||||
** Delete the given Select structure and all of its substructures.
|
||||
*/
|
||||
void sqlite3SelectDelete(sqlite3 *db, Select *p){
|
||||
clearSelect(db, p, 1);
|
||||
if( p ) clearSelect(db, p, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -589,9 +590,30 @@ static void pushOntoSorter(
|
||||
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
||||
if( iLimit ){
|
||||
int addr;
|
||||
int r1 = 0;
|
||||
/* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
|
||||
** register is initialized with value of LIMIT+OFFSET.) After the sorter
|
||||
** fills up, delete the least entry in the sorter after each insert.
|
||||
** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
||||
if( pSort->bOrderedInnerLoop ){
|
||||
r1 = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
|
||||
VdbeComment((v, "seq"));
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
||||
if( pSort->bOrderedInnerLoop ){
|
||||
/* If the inner loop is driven by an index such that values from
|
||||
** the same iteration of the inner loop are in sorted order, then
|
||||
** immediately jump to the next iteration of an inner loop if the
|
||||
** entry from the current iteration does not fit into the top
|
||||
** LIMIT+OFFSET entries of the sorter. */
|
||||
int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
@@ -637,30 +659,6 @@ static void codeDistinct(
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
/*
|
||||
** Generate an error message when a SELECT is used within a subexpression
|
||||
** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
|
||||
** column. We do this in a subroutine because the error used to occur
|
||||
** in multiple places. (The error only occurs in one place now, but we
|
||||
** retain the subroutine to minimize code disruption.)
|
||||
*/
|
||||
static int checkForMultiColumnSelectError(
|
||||
Parse *pParse, /* Parse context. */
|
||||
SelectDest *pDest, /* Destination of SELECT results */
|
||||
int nExpr /* Number of result columns returned by SELECT */
|
||||
){
|
||||
int eDest = pDest->eDest;
|
||||
if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
|
||||
sqlite3ErrorMsg(pParse, "only a single result allowed for "
|
||||
"a SELECT that is part of an expression");
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine generates the code for the inside of the inner loop
|
||||
** of a SELECT.
|
||||
@@ -870,19 +868,19 @@ static void selectInnerLoop(
|
||||
** item into the set table with bogus data.
|
||||
*/
|
||||
case SRT_Set: {
|
||||
assert( nResultCol==1 );
|
||||
pDest->affSdst =
|
||||
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
|
||||
if( pSort ){
|
||||
/* At first glance you would think we could optimize out the
|
||||
** ORDER BY in this case since the order of entries in the set
|
||||
** does not matter. But there might be a LIMIT clause, in which
|
||||
** case the order does matter */
|
||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
||||
pushOntoSorter(
|
||||
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
|
||||
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
||||
r1, pDest->zAffSdst, nResultCol);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -898,13 +896,14 @@ static void selectInnerLoop(
|
||||
}
|
||||
|
||||
/* If this is a scalar select that is part of an expression, then
|
||||
** store the results in the appropriate memory cell and break out
|
||||
** of the scan loop.
|
||||
** store the results in the appropriate memory cell or array of
|
||||
** memory cells and break out of the scan loop.
|
||||
*/
|
||||
case SRT_Mem: {
|
||||
assert( nResultCol==1 );
|
||||
assert( nResultCol==pDest->nSdst );
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
||||
pushOntoSorter(
|
||||
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
|
||||
}else{
|
||||
assert( regResult==iParm );
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
@@ -1006,7 +1005,7 @@ static void selectInnerLoop(
|
||||
*/
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
|
||||
KeyInfo *p = sqlite3Malloc(sizeof(KeyInfo) + nExtra);
|
||||
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
|
||||
if( p ){
|
||||
p->aSortOrder = (u8*)&p->aColl[N+X];
|
||||
p->nField = (u16)N;
|
||||
@@ -1028,7 +1027,7 @@ void sqlite3KeyInfoUnref(KeyInfo *p){
|
||||
if( p ){
|
||||
assert( p->nRef>0 );
|
||||
p->nRef--;
|
||||
if( p->nRef==0 ) sqlite3DbFree(0, p);
|
||||
if( p->nRef==0 ) sqlite3DbFree(p->db, p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,21 +1218,21 @@ static void generateSortTail(
|
||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||
}
|
||||
iTab = pSort->iECursor;
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
|
||||
regRowid = 0;
|
||||
regRow = pDest->iSdst;
|
||||
nSortData = nColumn;
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
nSortData = 1;
|
||||
regRow = sqlite3GetTempRange(pParse, nColumn);
|
||||
nSortData = nColumn;
|
||||
}
|
||||
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
int regSortOut = ++pParse->nMem;
|
||||
iSortTab = pParse->nTab++;
|
||||
if( pSort->labelBkOut ){
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
@@ -1261,16 +1260,14 @@ static void generateSortTail(
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case SRT_Set: {
|
||||
assert( nColumn==1 );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid,
|
||||
&pDest->affSdst, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
|
||||
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
|
||||
pDest->zAffSdst, nColumn);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
||||
break;
|
||||
}
|
||||
case SRT_Mem: {
|
||||
assert( nColumn==1 );
|
||||
sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
|
||||
/* The LIMIT clause will terminate the loop for us */
|
||||
break;
|
||||
}
|
||||
@@ -1289,7 +1286,11 @@ static void generateSortTail(
|
||||
}
|
||||
}
|
||||
if( regRowid ){
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
if( eDest==SRT_Set ){
|
||||
sqlite3ReleaseTempRange(pParse, regRow, nColumn);
|
||||
}else{
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
}
|
||||
/* The bottom of the loop
|
||||
@@ -1429,20 +1430,20 @@ static const char *columnTypeImpl(
|
||||
zType = "INTEGER";
|
||||
zOrigCol = "rowid";
|
||||
}else{
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
zOrigCol = pTab->aCol[iCol].zName;
|
||||
zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
|
||||
estWidth = pTab->aCol[iCol].szEst;
|
||||
}
|
||||
zOrigTab = pTab->zName;
|
||||
if( pNC->pParse ){
|
||||
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
|
||||
zOrigDb = pNC->pParse->db->aDb[iDb].zName;
|
||||
zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
|
||||
}
|
||||
#else
|
||||
if( iCol<0 ){
|
||||
zType = "INTEGER";
|
||||
}else{
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
|
||||
estWidth = pTab->aCol[iCol].szEst;
|
||||
}
|
||||
#endif
|
||||
@@ -1688,7 +1689,7 @@ int sqlite3ColumnsFromExprList(
|
||||
sqlite3DbFree(db, aCol);
|
||||
*paCol = 0;
|
||||
*pnCol = 0;
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -1704,7 +1705,7 @@ int sqlite3ColumnsFromExprList(
|
||||
** This routine requires that all identifiers in the SELECT
|
||||
** statement be resolved.
|
||||
*/
|
||||
static void selectAddColumnTypeAndCollation(
|
||||
void sqlite3SelectAddColumnTypeAndCollation(
|
||||
Parse *pParse, /* Parsing contexts */
|
||||
Table *pTab, /* Add column type information to this table */
|
||||
Select *pSelect /* SELECT used to determine types and collations */
|
||||
@@ -1726,13 +1727,20 @@ static void selectAddColumnTypeAndCollation(
|
||||
sNC.pSrcList = pSelect->pSrc;
|
||||
a = pSelect->pEList->a;
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const char *zType;
|
||||
int n, m;
|
||||
p = a[i].pExpr;
|
||||
if( pCol->zType==0 ){
|
||||
pCol->zType = sqlite3DbStrDup(db,
|
||||
columnType(&sNC, p,0,0,0, &pCol->szEst));
|
||||
}
|
||||
zType = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
|
||||
szAll += pCol->szEst;
|
||||
pCol->affinity = sqlite3ExprAffinity(p);
|
||||
if( zType && (m = sqlite3Strlen30(zType))>0 ){
|
||||
n = sqlite3Strlen30(pCol->zName);
|
||||
pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2);
|
||||
if( pCol->zName ){
|
||||
memcpy(&pCol->zName[n+1], zType, m+1);
|
||||
pCol->colFlags |= COLFLAG_HASTYPE;
|
||||
}
|
||||
}
|
||||
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
|
||||
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||
if( pColl && pCol->zColl==0 ){
|
||||
@@ -1769,7 +1777,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
pTab->zName = 0;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
||||
pTab->iPKey = -1;
|
||||
if( db->mallocFailed ){
|
||||
sqlite3DeleteTable(db, pTab);
|
||||
@@ -1782,20 +1790,20 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
** Get a VDBE for the given parser context. Create a new one if necessary.
|
||||
** If an error occurs, return NULL and leave a message in pParse.
|
||||
*/
|
||||
Vdbe *sqlite3GetVdbe(Parse *pParse){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
if( v==0 ){
|
||||
v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
|
||||
if( v ) sqlite3VdbeAddOp0(v, OP_Init);
|
||||
if( pParse->pToplevel==0
|
||||
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
|
||||
){
|
||||
pParse->okConstFactor = 1;
|
||||
}
|
||||
|
||||
static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
|
||||
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
|
||||
if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
|
||||
if( pParse->pToplevel==0
|
||||
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
|
||||
){
|
||||
pParse->okConstFactor = 1;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
Vdbe *sqlite3GetVdbe(Parse *pParse){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
return v ? v : allocVdbe(pParse);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -1845,8 +1853,9 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
||||
VdbeComment((v, "LIMIT counter"));
|
||||
if( n==0 ){
|
||||
sqlite3VdbeGoto(v, iBreak);
|
||||
}else if( n>=0 && p->nSelectRow>(u64)n ){
|
||||
p->nSelectRow = n;
|
||||
}else if( n>=0 && p->nSelectRow>sqlite3LogEst((u64)n) ){
|
||||
p->nSelectRow = sqlite3LogEst((u64)n);
|
||||
p->selFlags |= SF_FixedLimit;
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, p->pLimit, iLimit);
|
||||
@@ -2224,7 +2233,6 @@ static int multiSelect(
|
||||
if( dest.eDest==SRT_EphemTab ){
|
||||
assert( p->pEList );
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
dest.eDest = SRT_Table;
|
||||
}
|
||||
|
||||
@@ -2287,12 +2295,12 @@ static int multiSelect(
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->nSelectRow += pPrior->nSelectRow;
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
if( pPrior->pLimit
|
||||
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
|
||||
&& nLimit>0 && p->nSelectRow > (u64)nLimit
|
||||
&& nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
|
||||
){
|
||||
p->nSelectRow = nLimit;
|
||||
p->nSelectRow = sqlite3LogEst((u64)nLimit);
|
||||
}
|
||||
if( addr ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
@@ -2364,7 +2372,9 @@ static int multiSelect(
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->pOrderBy = 0;
|
||||
if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow;
|
||||
if( p->op==TK_UNION ){
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
}
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->pOffset = pOffset;
|
||||
@@ -2499,7 +2509,7 @@ static int multiSelect(
|
||||
nCol = p->pEList->nExpr;
|
||||
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
|
||||
if( !pKeyInfo ){
|
||||
rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto multi_select_end;
|
||||
}
|
||||
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
|
||||
@@ -2621,18 +2631,15 @@ static int generateOutputSubroutine(
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
|
||||
** then there should be a single item on the stack. Write this
|
||||
** item into the set table with bogus data.
|
||||
/* If we are creating a set for an "expr IN (SELECT ...)".
|
||||
*/
|
||||
case SRT_Set: {
|
||||
int r1;
|
||||
assert( pIn->nSdst==1 || pParse->nErr>0 );
|
||||
pDest->affSdst =
|
||||
sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
|
||||
testcase( pIn->nSdst>1 );
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
|
||||
r1, pDest->zAffSdst, pIn->nSdst);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
break;
|
||||
@@ -2854,7 +2861,7 @@ static int multiSelectOrderBy(
|
||||
}
|
||||
if( j==nOrderBy ){
|
||||
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
if( pNew==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pNew->flags |= EP_IntValue;
|
||||
pNew->u.iValue = i;
|
||||
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
|
||||
@@ -3001,7 +3008,7 @@ static int multiSelectOrderBy(
|
||||
addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, addrEofA);
|
||||
p->nSelectRow += pPrior->nSelectRow;
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
}
|
||||
|
||||
/* Generate a subroutine to run when the results from select B
|
||||
@@ -3688,12 +3695,13 @@ static int flattenSubquery(
|
||||
assert( pParent->pHaving==0 );
|
||||
pParent->pHaving = pParent->pWhere;
|
||||
pParent->pWhere = pWhere;
|
||||
pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
|
||||
sqlite3ExprDup(db, pSub->pHaving, 0));
|
||||
pParent->pHaving = sqlite3ExprAnd(db,
|
||||
sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving
|
||||
);
|
||||
assert( pParent->pGroupBy==0 );
|
||||
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
|
||||
}else{
|
||||
pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
|
||||
pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
|
||||
}
|
||||
substSelect(db, pParent, iParent, pSub->pEList, 0);
|
||||
|
||||
@@ -3776,12 +3784,18 @@ static int pushDownWhereTerms(
|
||||
){
|
||||
Expr *pNew;
|
||||
int nChng = 0;
|
||||
Select *pX; /* For looping over compound SELECTs in pSubq */
|
||||
if( pWhere==0 ) return 0;
|
||||
if( (pSubq->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
|
||||
return 0; /* restrictions (1) and (2) */
|
||||
for(pX=pSubq; pX; pX=pX->pPrior){
|
||||
if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
|
||||
testcase( pX->selFlags & SF_Aggregate );
|
||||
testcase( pX->selFlags & SF_Recursive );
|
||||
testcase( pX!=pSubq );
|
||||
return 0; /* restrictions (1) and (2) */
|
||||
}
|
||||
}
|
||||
if( pSubq->pLimit!=0 ){
|
||||
return 0; /* restriction (3) */
|
||||
return 0; /* restriction (3) */
|
||||
}
|
||||
while( pWhere->op==TK_AND ){
|
||||
nChng += pushDownWhereTerms(db, pSubq, pWhere->pRight, iCursor);
|
||||
@@ -4091,7 +4105,7 @@ static int withExpand(
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
||||
if( db->mallocFailed ) return SQLITE_NOMEM;
|
||||
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
|
||||
assert( pFrom->pSelect );
|
||||
|
||||
/* Check if this is a recursive CTE. */
|
||||
@@ -4377,7 +4391,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
continue;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
|
||||
zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*";
|
||||
}
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
char *zName = pTab->aCol[j].zName;
|
||||
@@ -4551,7 +4565,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
||||
Select *pSel = pFrom->pSelect;
|
||||
if( pSel ){
|
||||
while( pSel->pPrior ) pSel = pSel->pPrior;
|
||||
selectAddColumnTypeAndCollation(pParse, pTab, pSel);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4860,16 +4874,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* If writing to memory or generating a set
|
||||
** only a single column may be output.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
|
||||
goto select_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Try to flatten subqueries in the FROM clause up into the main query
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
@@ -4971,10 +4975,24 @@ int sqlite3Select(
|
||||
}
|
||||
|
||||
/* Generate code to implement the subquery
|
||||
**
|
||||
** The subquery is implemented as a co-routine if all of these are true:
|
||||
** (1) The subquery is guaranteed to be the outer loop (so that it
|
||||
** does not need to be computed more than once)
|
||||
** (2) The ALL keyword after SELECT is omitted. (Applications are
|
||||
** allowed to say "SELECT ALL" instead of just "SELECT" to disable
|
||||
** the use of co-routines.)
|
||||
** (3) Co-routines are not disabled using sqlite3_test_control()
|
||||
** with SQLITE_TESTCTRL_OPTIMIZATIONS.
|
||||
**
|
||||
** TODO: Are there other reasons beside (1) to use a co-routine
|
||||
** implementation?
|
||||
*/
|
||||
if( pTabList->nSrc==1
|
||||
&& (p->selFlags & SF_All)==0
|
||||
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
||||
if( i==0
|
||||
&& (pTabList->nSrc==1
|
||||
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
|
||||
&& (p->selFlags & SF_All)==0 /* (2) */
|
||||
&& OptimizationEnabled(db, SQLITE_SubqCoroutine) /* (3) */
|
||||
){
|
||||
/* Implement a co-routine that will return a single row of the result
|
||||
** set on each invocation.
|
||||
@@ -4987,7 +5005,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->fg.viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeEndCoroutine(v, pItem->regReturn);
|
||||
@@ -5010,7 +5028,7 @@ int sqlite3Select(
|
||||
/* If the subquery is not correlated and if we are not inside of
|
||||
** a trigger, then we only need to compute the value of the subquery
|
||||
** once. */
|
||||
onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
}else{
|
||||
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
|
||||
@@ -5018,7 +5036,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
@@ -5069,6 +5087,13 @@ int sqlite3Select(
|
||||
** the sDistinct.isTnct is still set. Hence, isTnct represents the
|
||||
** original setting of the SF_Distinct flag, not the current setting */
|
||||
assert( sDistinct.isTnct );
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If there is an ORDER BY clause, then create an ephemeral index to
|
||||
@@ -5101,7 +5126,7 @@ int sqlite3Select(
|
||||
/* Set the limiter.
|
||||
*/
|
||||
iEnd = sqlite3VdbeMakeLabel(v);
|
||||
p->nSelectRow = LARGEST_INT64;
|
||||
p->nSelectRow = 320; /* 4 billion rows */
|
||||
computeLimitRegisters(pParse, p, iEnd);
|
||||
if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
|
||||
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
|
||||
@@ -5125,10 +5150,12 @@ int sqlite3Select(
|
||||
if( !isAgg && pGroupBy==0 ){
|
||||
/* No aggregate functions and no GROUP BY clause */
|
||||
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
|
||||
assert( WHERE_USE_LIMIT==SF_FixedLimit );
|
||||
wctrlFlags |= p->selFlags & SF_FixedLimit;
|
||||
|
||||
/* Begin the database scan. */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
|
||||
p->pEList, wctrlFlags, 0);
|
||||
p->pEList, wctrlFlags, p->nSelectRow);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
|
||||
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
|
||||
@@ -5138,6 +5165,7 @@ int sqlite3Select(
|
||||
}
|
||||
if( sSort.pOrderBy ){
|
||||
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
|
||||
sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
|
||||
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
@@ -5188,9 +5216,11 @@ int sqlite3Select(
|
||||
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
|
||||
pItem->u.x.iAlias = 0;
|
||||
}
|
||||
if( p->nSelectRow>100 ) p->nSelectRow = 100;
|
||||
assert( 66==sqlite3LogEst(100) );
|
||||
if( p->nSelectRow>66 ) p->nSelectRow = 66;
|
||||
}else{
|
||||
p->nSelectRow = 1;
|
||||
assert( 0==sqlite3LogEst(1) );
|
||||
p->nSelectRow = 0;
|
||||
}
|
||||
|
||||
/* If there is both a GROUP BY and an ORDER BY clause and they are
|
||||
|
||||
+1065
-267
File diff suppressed because it is too large
Load Diff
+441
-46
@@ -30,8 +30,8 @@
|
||||
** the version number) and changes its name to "sqlite3.h" as
|
||||
** part of the build process.
|
||||
*/
|
||||
#ifndef _SQLITE3_H_
|
||||
#define _SQLITE3_H_
|
||||
#ifndef SQLITE3_H
|
||||
#define SQLITE3_H
|
||||
#include <stdarg.h> /* Needed for the definition of va_list */
|
||||
|
||||
/*
|
||||
@@ -54,8 +54,17 @@ extern "C" {
|
||||
#ifndef SQLITE_CDECL
|
||||
# define SQLITE_CDECL
|
||||
#endif
|
||||
#ifndef SQLITE_APICALL
|
||||
# define SQLITE_APICALL
|
||||
#endif
|
||||
#ifndef SQLITE_STDCALL
|
||||
# define SQLITE_STDCALL
|
||||
# define SQLITE_STDCALL SQLITE_APICALL
|
||||
#endif
|
||||
#ifndef SQLITE_CALLBACK
|
||||
# define SQLITE_CALLBACK
|
||||
#endif
|
||||
#ifndef SQLITE_SYSAPI
|
||||
# define SQLITE_SYSAPI
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -99,7 +108,8 @@ extern "C" {
|
||||
** be held constant and Z will be incremented or else Y will be incremented
|
||||
** and Z will be reset to zero.
|
||||
**
|
||||
** Since version 3.6.18, SQLite source code has been stored in the
|
||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||
** SQLite source code has been stored in the
|
||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||
** a string which identifies a particular check-in of SQLite
|
||||
@@ -443,7 +453,8 @@ int sqlite3_exec(
|
||||
** [result codes]. However, experience has shown that many of
|
||||
** these result codes are too coarse-grained. They do not provide as
|
||||
** much information about problems as programmers might like. In an effort to
|
||||
** address this, newer versions of SQLite (version 3.3.8 and later) include
|
||||
** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
|
||||
** and later) include
|
||||
** support for additional result codes that provide more detailed information
|
||||
** about errors. These [extended result codes] are enabled or disabled
|
||||
** on a per database connection basis using the
|
||||
@@ -506,6 +517,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
||||
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags For File Open Operations
|
||||
@@ -966,6 +978,12 @@ struct sqlite3_io_methods {
|
||||
** on whether or not the file has been renamed, moved, or deleted since it
|
||||
** was first opened.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
|
||||
** underlying native file handle associated with a file handle. This file
|
||||
** control interprets its argument as a pointer to a native file handle and
|
||||
** writes the resulting value there.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
|
||||
** opcode causes the xFileControl method to swap the file handle with the one
|
||||
@@ -1016,6 +1034,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_RBU 26
|
||||
#define SQLITE_FCNTL_VFS_POINTER 27
|
||||
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
||||
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
@@ -1035,6 +1054,16 @@ struct sqlite3_io_methods {
|
||||
*/
|
||||
typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Loadable Extension Thunk
|
||||
**
|
||||
** A pointer to the opaque sqlite3_api_routines structure is passed as
|
||||
** the third parameter to entry points of [loadable extensions]. This
|
||||
** structure must be typedefed in order to work around compiler warnings
|
||||
** on some platforms.
|
||||
*/
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** CAPI3REF: OS Interface Object
|
||||
**
|
||||
@@ -1228,7 +1257,7 @@ struct sqlite3_vfs {
|
||||
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
|
||||
/*
|
||||
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
|
||||
** New fields may be appended in figure versions. The iVersion
|
||||
** New fields may be appended in future versions. The iVersion
|
||||
** value will increment whenever this happens.
|
||||
*/
|
||||
};
|
||||
@@ -1820,6 +1849,20 @@ struct sqlite3_mem_methods {
|
||||
** is enabled (using the [PRAGMA threads] command) and the amount of content
|
||||
** to be sorted exceeds the page size times the minimum of the
|
||||
** [PRAGMA cache_size] setting and this value.
|
||||
**
|
||||
** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
|
||||
** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
|
||||
** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
|
||||
** becomes the [statement journal] spill-to-disk threshold.
|
||||
** [Statement journals] are held in memory until their size (in bytes)
|
||||
** exceeds this threshold, at which point they are written to disk.
|
||||
** Or if the threshold is -1, statement journals are always held
|
||||
** exclusively in memory.
|
||||
** Since many statement journals never become large, setting the spill
|
||||
** threshold to a value such as 64KiB can greatly reduce the amount of
|
||||
** I/O required to support statement rollback.
|
||||
** The default value for this setting is controlled by the
|
||||
** [SQLITE_STMTJRNL_SPILL] compile-time option.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
@@ -1847,6 +1890,7 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
||||
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
||||
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@@ -1904,11 +1948,53 @@ struct sqlite3_mem_methods {
|
||||
** following this call. The second parameter may be a NULL pointer, in
|
||||
** which case the trigger setting is not reported back. </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
||||
** <dd> ^This option is used to enable or disable the two-argument
|
||||
** version of the [fts3_tokenizer()] function which is part of the
|
||||
** [FTS3] full-text search engine extension.
|
||||
** There should be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
||||
** positive to enable fts3_tokenizer() or negative to leave the setting
|
||||
** unchanged.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
|
||||
** following this call. The second parameter may be a NULL pointer, in
|
||||
** which case the new setting is not reported back. </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
|
||||
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
|
||||
** interface independently of the [load_extension()] SQL function.
|
||||
** The [sqlite3_enable_load_extension()] API enables or disables both the
|
||||
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
|
||||
** There should be two additional arguments.
|
||||
** When the first argument to this interface is 1, then only the C-API is
|
||||
** enabled and the SQL function remains disabled. If the first argument to
|
||||
** this interface is 0, then both the C-API and the SQL function are disabled.
|
||||
** If the first argument is -1, then no changes are made to state of either the
|
||||
** C-API or the SQL function.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
|
||||
** is disabled or enabled following this call. The second parameter may
|
||||
** be a NULL pointer, in which case the new setting is not reported back.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
||||
** <dd> ^This option is used to change the name of the "main" database
|
||||
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
||||
** which will become the new schema name in place of "main". ^SQLite
|
||||
** does not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into this DBCONFIG option is unchanged
|
||||
** until after the database connection closes.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
|
||||
|
||||
|
||||
/*
|
||||
@@ -2185,7 +2271,7 @@ int sqlite3_complete16(const void *sql);
|
||||
** A busy handler must not close the database connection
|
||||
** or [prepared statement] that invoked the busy handler.
|
||||
*/
|
||||
int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
|
||||
int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Set A Busy Timeout
|
||||
@@ -2707,6 +2793,9 @@ int sqlite3_set_authorizer(
|
||||
** CAPI3REF: Tracing And Profiling Functions
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
|
||||
** instead of the routines described here.
|
||||
**
|
||||
** These routines register callback functions that can be used for
|
||||
** tracing and profiling the execution of SQL statements.
|
||||
**
|
||||
@@ -2732,10 +2821,104 @@ int sqlite3_set_authorizer(
|
||||
** sqlite3_profile() function is considered experimental and is
|
||||
** subject to change in future versions of SQLite.
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
|
||||
SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
|
||||
SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
|
||||
void(*xTrace)(void*,const char*), void*);
|
||||
SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
|
||||
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Trace Event Codes
|
||||
** KEYWORDS: SQLITE_TRACE
|
||||
**
|
||||
** These constants identify classes of events that can be monitored
|
||||
** using the [sqlite3_trace_v2()] tracing logic. The third argument
|
||||
** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
|
||||
** the following constants. ^The first argument to the trace callback
|
||||
** is one of the following constants.
|
||||
**
|
||||
** New tracing constants may be added in future releases.
|
||||
**
|
||||
** ^A trace callback has four arguments: xCallback(T,C,P,X).
|
||||
** ^The T argument is one of the integer type codes above.
|
||||
** ^The C argument is a copy of the context pointer passed in as the
|
||||
** fourth argument to [sqlite3_trace_v2()].
|
||||
** The P and X arguments are pointers whose meanings depend on T.
|
||||
**
|
||||
** <dl>
|
||||
** [[SQLITE_TRACE_STMT]] <dt>SQLITE_TRACE_STMT</dt>
|
||||
** <dd>^An SQLITE_TRACE_STMT callback is invoked when a prepared statement
|
||||
** first begins running and possibly at other times during the
|
||||
** execution of the prepared statement, such as at the start of each
|
||||
** trigger subprogram. ^The P argument is a pointer to the
|
||||
** [prepared statement]. ^The X argument is a pointer to a string which
|
||||
** is the unexpanded SQL text of the prepared statement or an SQL comment
|
||||
** that indicates the invocation of a trigger. ^The callback can compute
|
||||
** the same text that would have been returned by the legacy [sqlite3_trace()]
|
||||
** interface by using the X argument when X begins with "--" and invoking
|
||||
** [sqlite3_expanded_sql(P)] otherwise.
|
||||
**
|
||||
** [[SQLITE_TRACE_PROFILE]] <dt>SQLITE_TRACE_PROFILE</dt>
|
||||
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
|
||||
** information as is provided by the [sqlite3_profile()] callback.
|
||||
** ^The P argument is a pointer to the [prepared statement] and the
|
||||
** X argument points to a 64-bit integer which is the estimated of
|
||||
** the number of nanosecond that the prepared statement took to run.
|
||||
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
|
||||
**
|
||||
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
|
||||
** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
|
||||
** statement generates a single row of result.
|
||||
** ^The P argument is a pointer to the [prepared statement] and the
|
||||
** X argument is unused.
|
||||
**
|
||||
** [[SQLITE_TRACE_CLOSE]] <dt>SQLITE_TRACE_CLOSE</dt>
|
||||
** <dd>^An SQLITE_TRACE_CLOSE callback is invoked when a database
|
||||
** connection closes.
|
||||
** ^The P argument is a pointer to the [database connection] object
|
||||
** and the X argument is unused.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_TRACE_STMT 0x01
|
||||
#define SQLITE_TRACE_PROFILE 0x02
|
||||
#define SQLITE_TRACE_ROW 0x04
|
||||
#define SQLITE_TRACE_CLOSE 0x08
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Trace Hook
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback
|
||||
** function X against [database connection] D, using property mask M
|
||||
** and context pointer P. ^If the X callback is
|
||||
** NULL or if the M mask is zero, then tracing is disabled. The
|
||||
** M argument should be the bitwise OR-ed combination of
|
||||
** zero or more [SQLITE_TRACE] constants.
|
||||
**
|
||||
** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
|
||||
** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
|
||||
**
|
||||
** ^The X callback is invoked whenever any of the events identified by
|
||||
** mask M occur. ^The integer return value from the callback is currently
|
||||
** ignored, though this may change in future releases. Callback
|
||||
** implementations should return zero to ensure future compatibility.
|
||||
**
|
||||
** ^A trace callback is invoked with four arguments: callback(T,C,P,X).
|
||||
** ^The T argument is one of the [SQLITE_TRACE]
|
||||
** constants to indicate why the callback was invoked.
|
||||
** ^The C argument is a copy of the context pointer.
|
||||
** The P and X arguments are pointers whose meanings depend on T.
|
||||
**
|
||||
** The sqlite3_trace_v2() interface is intended to replace the legacy
|
||||
** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which
|
||||
** are deprecated.
|
||||
*/
|
||||
int sqlite3_trace_v2(
|
||||
sqlite3*,
|
||||
unsigned uMask,
|
||||
int(*xCallback)(unsigned,void*,void*,void*),
|
||||
void *pCtx
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Query Progress Callbacks
|
||||
** METHOD: sqlite3
|
||||
@@ -3354,11 +3537,35 @@ int sqlite3_prepare16_v2(
|
||||
** CAPI3REF: Retrieving Statement SQL
|
||||
** METHOD: sqlite3_stmt
|
||||
**
|
||||
** ^This interface can be used to retrieve a saved copy of the original
|
||||
** SQL text used to create a [prepared statement] if that statement was
|
||||
** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
|
||||
** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8
|
||||
** SQL text used to create [prepared statement] P if P was
|
||||
** created by either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
|
||||
** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
|
||||
** string containing the SQL text of prepared statement P with
|
||||
** [bound parameters] expanded.
|
||||
**
|
||||
** ^(For example, if a prepared statement is created using the SQL
|
||||
** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
|
||||
** and parameter :xyz is unbound, then sqlite3_sql() will return
|
||||
** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql()
|
||||
** will return "SELECT 2345,NULL".)^
|
||||
**
|
||||
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
|
||||
** is available to hold the result, or if the result would exceed the
|
||||
** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
||||
**
|
||||
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
|
||||
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
||||
** option causes sqlite3_expanded_sql() to always return NULL.
|
||||
**
|
||||
** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
|
||||
** automatically freed when the prepared statement is finalized.
|
||||
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
|
||||
** is obtained from [sqlite3_malloc()] and must be free by the application
|
||||
** by passing it to [sqlite3_free()].
|
||||
*/
|
||||
const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
||||
char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine If An SQL Statement Writes The Database
|
||||
@@ -3853,7 +4060,8 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||
** sqlite3_step(). Failure to reset the prepared statement using
|
||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||
** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
||||
** sqlite3_step() began
|
||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||
** break because any application that ever receives an SQLITE_MISUSE error
|
||||
@@ -4516,12 +4724,13 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
|
||||
** SQLite will invoke the destructor function X with parameter P exactly
|
||||
** once, when the metadata is discarded.
|
||||
** SQLite is free to discard the metadata at any time, including: <ul>
|
||||
** <li> when the corresponding function parameter changes, or
|
||||
** <li> when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
|
||||
** SQL statement, or
|
||||
** <li> when sqlite3_set_auxdata() is invoked again on the same parameter, or
|
||||
** <li> during the original sqlite3_set_auxdata() call when a memory
|
||||
** allocation error occurs. </ul>)^
|
||||
** <li> ^(when the corresponding function parameter changes)^, or
|
||||
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
|
||||
** SQL statement)^, or
|
||||
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
|
||||
** parameter)^, or
|
||||
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
|
||||
** allocation error occurs.)^ </ul>
|
||||
**
|
||||
** Note the last bullet in particular. The destructor X in
|
||||
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
|
||||
@@ -5158,7 +5367,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
** ^The sqlite3_update_hook() interface registers a callback function
|
||||
** with the [database connection] identified by the first argument
|
||||
** to be invoked whenever a row is updated, inserted or deleted in
|
||||
** a rowid table.
|
||||
** a [rowid table].
|
||||
** ^Any callback set by a previous call to this function
|
||||
** for the same database connection is overridden.
|
||||
**
|
||||
@@ -5197,8 +5406,8 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
** on the same [database connection] D, or NULL for
|
||||
** the first call on D.
|
||||
**
|
||||
** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]
|
||||
** interfaces.
|
||||
** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()],
|
||||
** and [sqlite3_preupdate_hook()] interfaces.
|
||||
*/
|
||||
void *sqlite3_update_hook(
|
||||
sqlite3*,
|
||||
@@ -5215,7 +5424,8 @@ void *sqlite3_update_hook(
|
||||
** and disabled if the argument is false.)^
|
||||
**
|
||||
** ^Cache sharing is enabled and disabled for an entire process.
|
||||
** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
|
||||
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
|
||||
** In prior versions of SQLite,
|
||||
** sharing was enabled or disabled for each thread separately.
|
||||
**
|
||||
** ^(The cache sharing mode set by this interface effects all subsequent
|
||||
@@ -5309,7 +5519,8 @@ int sqlite3_db_release_memory(sqlite3*);
|
||||
** from the heap.
|
||||
** </ul>)^
|
||||
**
|
||||
** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
|
||||
** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
|
||||
** the soft heap limit is enforced
|
||||
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
|
||||
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
|
||||
** the soft heap limit is enforced on every memory allocation. Without
|
||||
@@ -5348,7 +5559,7 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
||||
** column exists. ^The sqlite3_table_column_metadata() interface returns
|
||||
** SQLITE_ERROR and if the specified column does not exist.
|
||||
** ^If the column-name parameter to sqlite3_table_column_metadata() is a
|
||||
** NULL pointer, then this routine simply checks for the existance of the
|
||||
** NULL pointer, then this routine simply checks for the existence of the
|
||||
** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
|
||||
** does not.
|
||||
**
|
||||
@@ -5445,9 +5656,18 @@ int sqlite3_table_column_metadata(
|
||||
** should free this memory by calling [sqlite3_free()].
|
||||
**
|
||||
** ^Extension loading must be enabled using
|
||||
** [sqlite3_enable_load_extension()] prior to calling this API,
|
||||
** [sqlite3_enable_load_extension()] or
|
||||
** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL)
|
||||
** prior to calling this API,
|
||||
** otherwise an error will be returned.
|
||||
**
|
||||
** <b>Security warning:</b> It is recommended that the
|
||||
** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
|
||||
** interface. The use of the [sqlite3_enable_load_extension()] interface
|
||||
** should be avoided. This will keep the SQL function [load_extension()]
|
||||
** disabled and prevent SQL injections from giving attackers
|
||||
** access to extension loading capabilities.
|
||||
**
|
||||
** See also the [load_extension() SQL function].
|
||||
*/
|
||||
int sqlite3_load_extension(
|
||||
@@ -5470,6 +5690,17 @@ int sqlite3_load_extension(
|
||||
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
|
||||
** to turn extension loading on and call it with onoff==0 to turn
|
||||
** it back off again.
|
||||
**
|
||||
** ^This interface enables or disables both the C-API
|
||||
** [sqlite3_load_extension()] and the SQL function [load_extension()].
|
||||
** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..)
|
||||
** to enable or disable only the C-API.)^
|
||||
**
|
||||
** <b>Security warning:</b> It is recommended that extension loading
|
||||
** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
|
||||
** rather than this interface, so the [load_extension()] SQL function
|
||||
** remains disabled. This will prevent SQL injections from giving attackers
|
||||
** access to extension loading capabilities.
|
||||
*/
|
||||
int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
||||
|
||||
@@ -5483,7 +5714,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
||||
**
|
||||
** ^(Even though the function prototype shows that xEntryPoint() takes
|
||||
** no arguments and returns void, SQLite invokes xEntryPoint() with three
|
||||
** arguments and expects and integer result as if the signature of the
|
||||
** arguments and expects an integer result as if the signature of the
|
||||
** entry point where as follows:
|
||||
**
|
||||
** <blockquote><pre>
|
||||
@@ -5509,7 +5740,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
||||
** See also: [sqlite3_reset_auto_extension()]
|
||||
** and [sqlite3_cancel_auto_extension()]
|
||||
*/
|
||||
int sqlite3_auto_extension(void (*xEntryPoint)(void));
|
||||
int sqlite3_auto_extension(void(*xEntryPoint)(void));
|
||||
|
||||
/*
|
||||
** CAPI3REF: Cancel Automatic Extension Loading
|
||||
@@ -5521,7 +5752,7 @@ int sqlite3_auto_extension(void (*xEntryPoint)(void));
|
||||
** unregistered and it returns 0 if X was not on the list of initialization
|
||||
** routines.
|
||||
*/
|
||||
int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void));
|
||||
int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
|
||||
|
||||
/*
|
||||
** CAPI3REF: Reset Automatic Extension Loading
|
||||
@@ -5683,13 +5914,15 @@ struct sqlite3_module {
|
||||
** the xUpdate method are automatically rolled back by SQLite.
|
||||
**
|
||||
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
|
||||
** structure for SQLite version 3.8.2. If a virtual table extension is
|
||||
** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
|
||||
** If a virtual table extension is
|
||||
** used with an SQLite version earlier than 3.8.2, the results of attempting
|
||||
** to read or write the estimatedRows field are undefined (but are likely
|
||||
** to included crashing the application). The estimatedRows field should
|
||||
** therefore only be used if [sqlite3_libversion_number()] returns a
|
||||
** value greater than or equal to 3008002. Similarly, the idxFlags field
|
||||
** was added for version 3.9.0. It may therefore only be used if
|
||||
** was added for [version 3.9.0] ([dateof:3.9.0]).
|
||||
** It may therefore only be used if
|
||||
** sqlite3_libversion_number() returns a value greater than or equal to
|
||||
** 3009000.
|
||||
*/
|
||||
@@ -6387,7 +6620,7 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
|
||||
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
|
||||
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
|
||||
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
|
||||
#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
|
||||
#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
|
||||
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
|
||||
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
|
||||
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
|
||||
@@ -6491,6 +6724,7 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
|
||||
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
|
||||
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
|
||||
#define SQLITE_TESTCTRL_BYTEORDER 22
|
||||
@@ -6697,6 +6931,18 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** memory used by all pager caches associated with the database connection.)^
|
||||
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
|
||||
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
|
||||
** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a
|
||||
** pager cache is shared between two or more connections the bytes of heap
|
||||
** memory used by that pager cache is divided evenly between the attached
|
||||
** connections.)^ In other words, if none of the pager caches associated
|
||||
** with the database connection are shared, this request returns the same
|
||||
** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are
|
||||
** shared, the value returned by this call will be smaller than that returned
|
||||
** by DBSTATUS_CACHE_USED. ^The highwater mark associated with
|
||||
** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
|
||||
** <dd>This parameter returns the approximate number of bytes of heap
|
||||
** memory used to store the schema for all databases associated
|
||||
@@ -6754,7 +7000,8 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
#define SQLITE_DBSTATUS_CACHE_MISS 8
|
||||
#define SQLITE_DBSTATUS_CACHE_WRITE 9
|
||||
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
|
||||
#define SQLITE_DBSTATUS_MAX 10 /* Largest defined DBSTATUS */
|
||||
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
|
||||
#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */
|
||||
|
||||
|
||||
/*
|
||||
@@ -7108,7 +7355,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
|
||||
** an error.
|
||||
**
|
||||
** ^A call to sqlite3_backup_init() will fail, returning SQLITE_ERROR, if
|
||||
** ^A call to sqlite3_backup_init() will fail, returning NULL, if
|
||||
** there is already a read or read-write transaction open on the
|
||||
** destination database.
|
||||
**
|
||||
@@ -7489,7 +7736,7 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
||||
** previously registered write-ahead log callback. ^Note that the
|
||||
** [sqlite3_wal_autocheckpoint()] interface and the
|
||||
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
|
||||
** those overwrite any prior [sqlite3_wal_hook()] settings.
|
||||
** overwrite any prior [sqlite3_wal_hook()] settings.
|
||||
*/
|
||||
void *sqlite3_wal_hook(
|
||||
sqlite3*,
|
||||
@@ -7886,6 +8133,114 @@ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
|
||||
*/
|
||||
int sqlite3_db_cacheflush(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: The pre-update hook.
|
||||
**
|
||||
** ^These interfaces are only available if SQLite is compiled using the
|
||||
** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
|
||||
**
|
||||
** ^The [sqlite3_preupdate_hook()] interface registers a callback function
|
||||
** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
|
||||
** on a [rowid table].
|
||||
** ^At most one preupdate hook may be registered at a time on a single
|
||||
** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
|
||||
** the previous setting.
|
||||
** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()]
|
||||
** with a NULL pointer as the second parameter.
|
||||
** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
|
||||
** the first parameter to callbacks.
|
||||
**
|
||||
** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
|
||||
** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
|
||||
** tables.
|
||||
**
|
||||
** ^The second parameter to the preupdate callback is a pointer to
|
||||
** the [database connection] that registered the preupdate hook.
|
||||
** ^The third parameter to the preupdate callback is one of the constants
|
||||
** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to identify the
|
||||
** kind of update operation that is about to occur.
|
||||
** ^(The fourth parameter to the preupdate callback is the name of the
|
||||
** database within the database connection that is being modified. This
|
||||
** will be "main" for the main database or "temp" for TEMP tables or
|
||||
** the name given after the AS keyword in the [ATTACH] statement for attached
|
||||
** databases.)^
|
||||
** ^The fifth parameter to the preupdate callback is the name of the
|
||||
** table that is being modified.
|
||||
** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
|
||||
** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
|
||||
** undefined for SQLITE_INSERT changes.
|
||||
** ^The seventh parameter to the preupdate callback is the final [rowid] of
|
||||
** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
|
||||
** undefined for SQLITE_DELETE changes.
|
||||
**
|
||||
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
|
||||
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
|
||||
** provide additional information about a preupdate event. These routines
|
||||
** may only be called from within a preupdate callback. Invoking any of
|
||||
** these routines from outside of a preupdate callback or with a
|
||||
** [database connection] pointer that is different from the one supplied
|
||||
** to the preupdate callback results in undefined and probably undesirable
|
||||
** behavior.
|
||||
**
|
||||
** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns
|
||||
** in the row that is being inserted, updated, or deleted.
|
||||
**
|
||||
** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to
|
||||
** a [protected sqlite3_value] that contains the value of the Nth column of
|
||||
** the table row before it is updated. The N parameter must be between 0
|
||||
** and one less than the number of columns or the behavior will be
|
||||
** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE
|
||||
** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the
|
||||
** behavior is undefined. The [sqlite3_value] that P points to
|
||||
** will be destroyed when the preupdate callback returns.
|
||||
**
|
||||
** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to
|
||||
** a [protected sqlite3_value] that contains the value of the Nth column of
|
||||
** the table row after it is updated. The N parameter must be between 0
|
||||
** and one less than the number of columns or the behavior will be
|
||||
** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE
|
||||
** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the
|
||||
** behavior is undefined. The [sqlite3_value] that P points to
|
||||
** will be destroyed when the preupdate callback returns.
|
||||
**
|
||||
** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
|
||||
** callback was invoked as a result of a direct insert, update, or delete
|
||||
** operation; or 1 for inserts, updates, or deletes invoked by top-level
|
||||
** triggers; or 2 for changes resulting from triggers called by top-level
|
||||
** triggers; and so forth.
|
||||
**
|
||||
** See also: [sqlite3_update_hook()]
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
|
||||
sqlite3 *db,
|
||||
void(*xPreUpdate)(
|
||||
void *pCtx, /* Copy of third arg to preupdate_hook() */
|
||||
sqlite3 *db, /* Database handle */
|
||||
int op, /* SQLITE_UPDATE, DELETE or INSERT */
|
||||
char const *zDb, /* Database name */
|
||||
char const *zName, /* Table name */
|
||||
sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
|
||||
sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
|
||||
),
|
||||
void*
|
||||
);
|
||||
SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
|
||||
SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
|
||||
SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *);
|
||||
SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Low-level system error code
|
||||
**
|
||||
** ^Attempt to return the underlying operating system error code or error
|
||||
** number that caused the most recent I/O error or failure to open a file.
|
||||
** The return value is OS-dependent. For example, on unix systems, after
|
||||
** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
|
||||
** called to get back the underlying "errno" that caused the problem, such
|
||||
** as ENOSPC, EAUTH, EISDIR, and so forth.
|
||||
*/
|
||||
int sqlite3_system_errno(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Snapshot
|
||||
** KEYWORDS: {snapshot}
|
||||
@@ -7944,17 +8299,30 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
||||
** CAPI3REF: Start a read transaction on an historical snapshot
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** ^The [sqlite3_snapshot_open(D,S,P)] interface attempts to move the
|
||||
** read transaction that is currently open on schema S of
|
||||
** [database connection] D so that it refers to historical [snapshot] P.
|
||||
** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
|
||||
** read transaction for schema S of
|
||||
** [database connection] D such that the read transaction
|
||||
** refers to historical [snapshot] P, rather than the most
|
||||
** recent change to the database.
|
||||
** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
|
||||
** or an appropriate [error code] if it fails.
|
||||
**
|
||||
** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
|
||||
** the first operation, apart from other sqlite3_snapshot_open() calls,
|
||||
** following the [BEGIN] that starts a new read transaction.
|
||||
** ^A [snapshot] will fail to open if it has been overwritten by a
|
||||
** [checkpoint].
|
||||
** the first operation following the [BEGIN] that takes the schema S
|
||||
** out of [autocommit mode].
|
||||
** ^In other words, schema S must not currently be in
|
||||
** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
|
||||
** database connection D must be out of [autocommit mode].
|
||||
** ^A [snapshot] will fail to open if it has been overwritten by a
|
||||
** [checkpoint].
|
||||
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
|
||||
** database connection D does not know that the database file for
|
||||
** schema S is in [WAL mode]. A database connection might not know
|
||||
** that the database file is in [WAL mode] if there has been no prior
|
||||
** I/O on that database connection, or if the database entered [WAL mode]
|
||||
** after the most recent I/O on the database connection.)^
|
||||
** (Hint: Run "[PRAGMA application_id]" against a newly opened
|
||||
** database connection in order to make it ready to use snapshots.)
|
||||
**
|
||||
** The [sqlite3_snapshot_open()] interface is only available when the
|
||||
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
||||
@@ -7978,6 +8346,33 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Compare the ages of two snapshot handles.
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
|
||||
** of two valid snapshot handles.
|
||||
**
|
||||
** If the two snapshot handles are not associated with the same database
|
||||
** file, the result of the comparison is undefined.
|
||||
**
|
||||
** Additionally, the result of the comparison is only valid if both of the
|
||||
** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
|
||||
** last time the wal file was deleted. The wal file is deleted when the
|
||||
** database is changed back to rollback mode or when the number of database
|
||||
** clients drops to zero. If either snapshot handle was obtained before the
|
||||
** wal file was last deleted, the value returned by this function
|
||||
** is undefined.
|
||||
**
|
||||
** Otherwise, this API returns a negative value if P1 refers to an older
|
||||
** snapshot than P2, zero if the two handles refer to the same database
|
||||
** snapshot, and a positive value if P1 is a newer snapshot than P2.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
||||
sqlite3_snapshot *p1,
|
||||
sqlite3_snapshot *p2
|
||||
);
|
||||
|
||||
/*
|
||||
** Undo the hack that converts floating point types to integer for
|
||||
** builds on processors without floating point support.
|
||||
@@ -7989,4 +8384,4 @@ SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _SQLITE3_H_ */
|
||||
#endif /* SQLITE3_H */
|
||||
|
||||
+23
-5
@@ -15,12 +15,10 @@
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
@@ -279,8 +277,23 @@ struct sqlite3_api_routines {
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
@@ -522,6 +535,11 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
@@ -539,4 +557,4 @@ struct sqlite3_api_routines {
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITE3EXT_H_ */
|
||||
#endif /* SQLITE3EXT_H */
|
||||
|
||||
+446
-228
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -101,13 +101,13 @@
|
||||
** The suggested maximum number of in-memory pages to use for
|
||||
** the main database table and for temporary tables.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-31093-59126 The default suggested cache size
|
||||
** is 2000 pages.
|
||||
** IMPLEMENTATION-OF: R-30185-15359 The default suggested cache size is -2000,
|
||||
** which means the cache size is limited to 2048000 bytes of memory.
|
||||
** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be
|
||||
** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options.
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_CACHE_SIZE
|
||||
# define SQLITE_DEFAULT_CACHE_SIZE 2000
|
||||
# define SQLITE_DEFAULT_CACHE_SIZE -2000
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -120,8 +120,9 @@
|
||||
|
||||
/*
|
||||
** The maximum number of attached databases. This must be between 0
|
||||
** and 62. The upper bound on 62 is because a 64-bit integer bitmap
|
||||
** is used internally to track attached databases.
|
||||
** and 125. The upper bound of 125 is because the attached databases are
|
||||
** counted using a signed 8-bit integer which has a maximum value of 127
|
||||
** and we have to allow 2 extra counts for the "main" and "temp" databases.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_ATTACHED
|
||||
# define SQLITE_MAX_ATTACHED 10
|
||||
@@ -156,7 +157,7 @@
|
||||
** The default size of a database page.
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_PAGE_SIZE
|
||||
# define SQLITE_DEFAULT_PAGE_SIZE 1024
|
||||
# define SQLITE_DEFAULT_PAGE_SIZE 4096
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
|
||||
# undef SQLITE_DEFAULT_PAGE_SIZE
|
||||
|
||||
+7
-2
@@ -158,7 +158,7 @@ int sqlite3_status64(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
|
||||
sqlite3_int64 iCur, iHwtr;
|
||||
sqlite3_int64 iCur = 0, iHwtr = 0;
|
||||
int rc;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT;
|
||||
@@ -219,6 +219,7 @@ int sqlite3_db_status(
|
||||
** by all pagers associated with the given database connection. The
|
||||
** highwater mark is meaningless and is returned as zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_USED_SHARED:
|
||||
case SQLITE_DBSTATUS_CACHE_USED: {
|
||||
int totalUsed = 0;
|
||||
int i;
|
||||
@@ -227,7 +228,11 @@ int sqlite3_db_status(
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
totalUsed += sqlite3PagerMemUsed(pPager);
|
||||
int nByte = sqlite3PagerMemUsed(pPager);
|
||||
if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){
|
||||
nByte = nByte / sqlite3BtreeConnectionCount(pBt);
|
||||
}
|
||||
totalUsed += nByte;
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
|
||||
+3
-3
@@ -101,7 +101,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
||||
return 0;
|
||||
|
||||
malloc_failed:
|
||||
p->rc = SQLITE_NOMEM;
|
||||
p->rc = SQLITE_NOMEM_BKPT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ int sqlite3_get_table(
|
||||
res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );
|
||||
if( res.azResult==0 ){
|
||||
db->errCode = SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
res.azResult[0] = 0;
|
||||
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
|
||||
@@ -171,7 +171,7 @@ int sqlite3_get_table(
|
||||
if( azNew==0 ){
|
||||
sqlite3_free_table(&res.azResult[1]);
|
||||
db->errCode = SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
res.azResult = azNew;
|
||||
}
|
||||
|
||||
+517
-152
File diff suppressed because it is too large
Load Diff
+551
-187
File diff suppressed because it is too large
Load Diff
+25
-21
@@ -14,7 +14,11 @@
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
@@ -38,7 +42,7 @@ static void pager_test_reiniter(DbPage *pNotUsed){
|
||||
**
|
||||
** Open a new pager
|
||||
*/
|
||||
static int pager_open(
|
||||
static int SQLITE_TCLAPI pager_open(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -75,7 +79,7 @@ static int pager_open(
|
||||
**
|
||||
** Close the given pager.
|
||||
*/
|
||||
static int pager_close(
|
||||
static int SQLITE_TCLAPI pager_close(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -102,7 +106,7 @@ static int pager_close(
|
||||
**
|
||||
** Rollback changes
|
||||
*/
|
||||
static int pager_rollback(
|
||||
static int SQLITE_TCLAPI pager_rollback(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -129,7 +133,7 @@ static int pager_rollback(
|
||||
**
|
||||
** Commit all changes
|
||||
*/
|
||||
static int pager_commit(
|
||||
static int SQLITE_TCLAPI pager_commit(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -161,7 +165,7 @@ static int pager_commit(
|
||||
**
|
||||
** Start a new checkpoint.
|
||||
*/
|
||||
static int pager_stmt_begin(
|
||||
static int SQLITE_TCLAPI pager_stmt_begin(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -188,7 +192,7 @@ static int pager_stmt_begin(
|
||||
**
|
||||
** Rollback changes to a checkpoint
|
||||
*/
|
||||
static int pager_stmt_rollback(
|
||||
static int SQLITE_TCLAPI pager_stmt_rollback(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -216,7 +220,7 @@ static int pager_stmt_rollback(
|
||||
**
|
||||
** Commit changes to a checkpoint
|
||||
*/
|
||||
static int pager_stmt_commit(
|
||||
static int SQLITE_TCLAPI pager_stmt_commit(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -243,7 +247,7 @@ static int pager_stmt_commit(
|
||||
**
|
||||
** Return pager statistics.
|
||||
*/
|
||||
static int pager_stats(
|
||||
static int SQLITE_TCLAPI pager_stats(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -276,7 +280,7 @@ static int pager_stats(
|
||||
**
|
||||
** Return the size of the database file.
|
||||
*/
|
||||
static int pager_pagecount(
|
||||
static int SQLITE_TCLAPI pager_pagecount(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -302,7 +306,7 @@ static int pager_pagecount(
|
||||
**
|
||||
** Return a pointer to a page from the database.
|
||||
*/
|
||||
static int page_get(
|
||||
static int SQLITE_TCLAPI page_get(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -339,7 +343,7 @@ static int page_get(
|
||||
** Return a pointer to a page if the page is already in cache.
|
||||
** If not in cache, return an empty string.
|
||||
*/
|
||||
static int page_lookup(
|
||||
static int SQLITE_TCLAPI page_lookup(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -367,7 +371,7 @@ static int page_lookup(
|
||||
/*
|
||||
** Usage: pager_truncate ID PGNO
|
||||
*/
|
||||
static int pager_truncate(
|
||||
static int SQLITE_TCLAPI pager_truncate(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -392,7 +396,7 @@ static int pager_truncate(
|
||||
**
|
||||
** Drop a pointer to a page.
|
||||
*/
|
||||
static int page_unref(
|
||||
static int SQLITE_TCLAPI page_unref(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -414,7 +418,7 @@ static int page_unref(
|
||||
**
|
||||
** Return the content of a page
|
||||
*/
|
||||
static int page_read(
|
||||
static int SQLITE_TCLAPI page_read(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -438,7 +442,7 @@ static int page_read(
|
||||
**
|
||||
** Return the page number for a page.
|
||||
*/
|
||||
static int page_number(
|
||||
static int SQLITE_TCLAPI page_number(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -462,7 +466,7 @@ static int page_number(
|
||||
**
|
||||
** Write something into a page.
|
||||
*/
|
||||
static int page_write(
|
||||
static int SQLITE_TCLAPI page_write(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -498,7 +502,7 @@ static int page_write(
|
||||
** new pages after N. If N is 2096 or bigger, this will test the
|
||||
** ability of SQLite to write to large files.
|
||||
*/
|
||||
static int fake_big_file(
|
||||
static int SQLITE_TCLAPI fake_big_file(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -551,7 +555,7 @@ static int fake_big_file(
|
||||
**
|
||||
** Set the PENDING_BYTE using the sqlite3_test_control() interface.
|
||||
*/
|
||||
static int testPendingByte(
|
||||
static int SQLITE_TCLAPI testPendingByte(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -616,7 +620,7 @@ static int faultSimCallback(int x){
|
||||
** appended, whenever sqlite3FaultSim() is called. Or, if SCRIPT is the
|
||||
** empty string, cancel the sqlite3FaultSim() callback.
|
||||
*/
|
||||
static int faultInstallCmd(
|
||||
static int SQLITE_TCLAPI faultInstallCmd(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -659,7 +663,7 @@ static int faultInstallCmd(
|
||||
** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control.
|
||||
** See comments on sqlite3BitvecBuiltinTest() for additional information.
|
||||
*/
|
||||
static int testBitvecBuiltinTest(
|
||||
static int SQLITE_TCLAPI testBitvecBuiltinTest(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
|
||||
+46
-44
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -33,7 +37,7 @@ static int nRefSqlite3 = 0;
|
||||
**
|
||||
** Open a new database
|
||||
*/
|
||||
static int btree_open(
|
||||
static int SQLITE_TCLAPI btree_open(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -79,7 +83,7 @@ static int btree_open(
|
||||
**
|
||||
** Close the given database.
|
||||
*/
|
||||
static int btree_close(
|
||||
static int SQLITE_TCLAPI btree_close(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -114,7 +118,7 @@ static int btree_close(
|
||||
**
|
||||
** Start a new transaction
|
||||
*/
|
||||
static int btree_begin_transaction(
|
||||
static int SQLITE_TCLAPI btree_begin_transaction(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -143,7 +147,7 @@ static int btree_begin_transaction(
|
||||
**
|
||||
** Returns pager statistics
|
||||
*/
|
||||
static int btree_pager_stats(
|
||||
static int SQLITE_TCLAPI btree_pager_stats(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -193,7 +197,7 @@ static int btree_pager_stats(
|
||||
**
|
||||
** Create a new cursor. Return the ID for the cursor.
|
||||
*/
|
||||
static int btree_cursor(
|
||||
static int SQLITE_TCLAPI btree_cursor(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -242,14 +246,13 @@ static int btree_cursor(
|
||||
**
|
||||
** Close a cursor opened using btree_cursor.
|
||||
*/
|
||||
static int btree_close_cursor(
|
||||
static int SQLITE_TCLAPI btree_close_cursor(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
BtCursor *pCur;
|
||||
Btree *pBt;
|
||||
int rc;
|
||||
|
||||
if( argc!=2 ){
|
||||
@@ -258,12 +261,18 @@ static int btree_close_cursor(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pCur = sqlite3TestTextToPtr(argv[1]);
|
||||
pBt = pCur->pBtree;
|
||||
sqlite3_mutex_enter(pBt->db->mutex);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
#if SQLITE_THREADSAFE>0
|
||||
{
|
||||
Btree *pBt = pCur->pBtree;
|
||||
sqlite3_mutex_enter(pBt->db->mutex);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeCloseCursor(pCur);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
sqlite3_mutex_leave(pBt->db->mutex);
|
||||
}
|
||||
#else
|
||||
rc = sqlite3BtreeCloseCursor(pCur);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
sqlite3_mutex_leave(pBt->db->mutex);
|
||||
#endif
|
||||
ckfree((char *)pCur);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
|
||||
@@ -279,7 +288,7 @@ static int btree_close_cursor(
|
||||
** or 1 if the cursor was already on the last entry in the table or if
|
||||
** the table is empty.
|
||||
*/
|
||||
static int btree_next(
|
||||
static int SQLITE_TCLAPI btree_next(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -314,7 +323,7 @@ static int btree_next(
|
||||
** Move the cursor to the first entry in the table. Return 0 if the
|
||||
** cursor was left point to something and 1 if the table is empty.
|
||||
*/
|
||||
static int btree_first(
|
||||
static int SQLITE_TCLAPI btree_first(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -349,7 +358,7 @@ static int btree_first(
|
||||
** Return TRUE if the given cursor is not pointing at a valid entry.
|
||||
** Return FALSE if the cursor does point to a valid entry.
|
||||
*/
|
||||
static int btree_eof(
|
||||
static int SQLITE_TCLAPI btree_eof(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -378,15 +387,14 @@ static int btree_eof(
|
||||
**
|
||||
** Return the number of bytes of payload
|
||||
*/
|
||||
static int btree_payload_size(
|
||||
static int SQLITE_TCLAPI btree_payload_size(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
BtCursor *pCur;
|
||||
int n2;
|
||||
u64 n1;
|
||||
u32 n;
|
||||
char zBuf[50];
|
||||
|
||||
if( argc!=2 ){
|
||||
@@ -396,17 +404,9 @@ static int btree_payload_size(
|
||||
}
|
||||
pCur = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pCur->pBtree);
|
||||
|
||||
/* The cursor may be in "require-seek" state. If this is the case, the
|
||||
** call to BtreeDataSize() will fix it. */
|
||||
sqlite3BtreeDataSize(pCur, (u32*)&n2);
|
||||
if( pCur->apPage[pCur->iPage]->intKey ){
|
||||
n1 = 0;
|
||||
}else{
|
||||
sqlite3BtreeKeySize(pCur, (i64*)&n1);
|
||||
}
|
||||
n = sqlite3BtreePayloadSize(pCur);
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf, "%u", n);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -425,7 +425,7 @@ static int btree_payload_size(
|
||||
** This command returns nothing if it works. It returns an error message
|
||||
** if something goes wrong.
|
||||
*/
|
||||
static int btree_varint_test(
|
||||
static int SQLITE_TCLAPI btree_varint_test(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -509,7 +509,7 @@ static int btree_varint_test(
|
||||
** sqlite3 db test.db
|
||||
** set bt [btree_from_db db]
|
||||
*/
|
||||
static int btree_from_db(
|
||||
static int SQLITE_TCLAPI btree_from_db(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -547,9 +547,9 @@ static int btree_from_db(
|
||||
/*
|
||||
** Usage: btree_ismemdb ID
|
||||
**
|
||||
** Return true if the B-Tree is in-memory.
|
||||
** Return true if the B-Tree is currently stored entirely in memory.
|
||||
*/
|
||||
static int btree_ismemdb(
|
||||
static int SQLITE_TCLAPI btree_ismemdb(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -557,6 +557,7 @@ static int btree_ismemdb(
|
||||
){
|
||||
Btree *pBt;
|
||||
int res;
|
||||
sqlite3_file *pFile;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
@@ -566,7 +567,8 @@ static int btree_ismemdb(
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3_mutex_enter(pBt->db->mutex);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
|
||||
pFile = sqlite3PagerFile(sqlite3BtreePager(pBt));
|
||||
res = (pFile->pMethods==0);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
sqlite3_mutex_leave(pBt->db->mutex);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
|
||||
@@ -578,7 +580,7 @@ static int btree_ismemdb(
|
||||
**
|
||||
** Set the size of the cache used by btree $ID.
|
||||
*/
|
||||
static int btree_set_cache_size(
|
||||
static int SQLITE_TCLAPI btree_set_cache_size(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -608,7 +610,7 @@ static int btree_set_cache_size(
|
||||
**
|
||||
** Set the size of the cache used by btree $ID.
|
||||
*/
|
||||
static int btree_insert(
|
||||
static int SQLITE_TCLAPI btree_insert(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -616,27 +618,27 @@ static int btree_insert(
|
||||
){
|
||||
BtCursor *pCur;
|
||||
int rc;
|
||||
void *pKey = 0;
|
||||
int nKey = 0;
|
||||
void *pData = 0;
|
||||
int nData = 0;
|
||||
BtreePayload x;
|
||||
|
||||
if( objc!=4 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
memset(&x, 0, sizeof(x));
|
||||
if( objc==4 ){
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &nKey) ) return TCL_ERROR;
|
||||
pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &nData);
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &rc) ) return TCL_ERROR;
|
||||
x.nKey = rc;
|
||||
x.pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &x.nData);
|
||||
}else{
|
||||
pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &nKey);
|
||||
x.pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &rc);
|
||||
x.nKey = rc;
|
||||
}
|
||||
pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
|
||||
sqlite3_mutex_enter(pCur->pBtree->db->mutex);
|
||||
sqlite3BtreeEnter(pCur->pBtree);
|
||||
rc = sqlite3BtreeInsert(pCur, pKey, nKey, pData, nData, 0, 0, 0);
|
||||
rc = sqlite3BtreeInsert(pCur, &x, 0, 0);
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
sqlite3_mutex_leave(pCur->pBtree->db->mutex);
|
||||
|
||||
|
||||
+20
-16
@@ -12,7 +12,11 @@
|
||||
** Code for testing the SQLite library in a multithreaded environment.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -121,7 +125,7 @@ static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
|
||||
** NAME should be an upper case letter. Start the thread running with
|
||||
** an open connection to the given database.
|
||||
*/
|
||||
static int tcl_thread_create(
|
||||
static int SQLITE_TCLAPI tcl_thread_create(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -170,7 +174,7 @@ static void thread_wait(Thread *p){
|
||||
**
|
||||
** Wait on thread ID to reach its idle state.
|
||||
*/
|
||||
static int tcl_thread_wait(
|
||||
static int SQLITE_TCLAPI tcl_thread_wait(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -214,7 +218,7 @@ static void stop_thread(Thread *p){
|
||||
** Cause a thread to shut itself down. Wait for the shutdown to be
|
||||
** completed. If ID is "*" then stop all threads.
|
||||
*/
|
||||
static int tcl_thread_halt(
|
||||
static int SQLITE_TCLAPI tcl_thread_halt(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -249,7 +253,7 @@ static int tcl_thread_halt(
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** number of columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_argc(
|
||||
static int SQLITE_TCLAPI tcl_thread_argc(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -281,7 +285,7 @@ static int tcl_thread_argc(
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** value of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_argv(
|
||||
static int SQLITE_TCLAPI tcl_thread_argv(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -317,7 +321,7 @@ static int tcl_thread_argv(
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** name of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_colname(
|
||||
static int SQLITE_TCLAPI tcl_thread_colname(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -353,7 +357,7 @@ static int tcl_thread_colname(
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** result code from that operation.
|
||||
*/
|
||||
static int tcl_thread_result(
|
||||
static int SQLITE_TCLAPI tcl_thread_result(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -385,7 +389,7 @@ static int tcl_thread_result(
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** error string.
|
||||
*/
|
||||
static int tcl_thread_error(
|
||||
static int SQLITE_TCLAPI tcl_thread_error(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -430,7 +434,7 @@ static void do_compile(Thread *p){
|
||||
**
|
||||
** Compile a new virtual machine.
|
||||
*/
|
||||
static int tcl_thread_compile(
|
||||
static int SQLITE_TCLAPI tcl_thread_compile(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -483,7 +487,7 @@ static void do_step(Thread *p){
|
||||
**
|
||||
** Advance the virtual machine by one step
|
||||
*/
|
||||
static int tcl_thread_step(
|
||||
static int SQLITE_TCLAPI tcl_thread_step(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -525,7 +529,7 @@ static void do_finalize(Thread *p){
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_thread_finalize(
|
||||
static int SQLITE_TCLAPI tcl_thread_finalize(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -556,7 +560,7 @@ static int tcl_thread_finalize(
|
||||
**
|
||||
** Interchange the sqlite* pointer between two threads.
|
||||
*/
|
||||
static int tcl_thread_swap(
|
||||
static int SQLITE_TCLAPI tcl_thread_swap(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -596,7 +600,7 @@ static int tcl_thread_swap(
|
||||
** remove the pointer from the thread itself. Afterwards, the thread
|
||||
** can be stopped and the connection can be used by the main thread.
|
||||
*/
|
||||
static int tcl_thread_db_get(
|
||||
static int SQLITE_TCLAPI tcl_thread_db_get(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -627,7 +631,7 @@ static int tcl_thread_db_get(
|
||||
** Usage: thread_db_put ID DB
|
||||
**
|
||||
*/
|
||||
static int tcl_thread_db_put(
|
||||
static int SQLITE_TCLAPI tcl_thread_db_put(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -659,7 +663,7 @@ static int tcl_thread_db_put(
|
||||
** Return the database stmt pointer for the given thread. Then
|
||||
** remove the pointer from the thread itself.
|
||||
*/
|
||||
static int tcl_thread_stmt_get(
|
||||
static int SQLITE_TCLAPI tcl_thread_stmt_get(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
|
||||
+9
-5
@@ -17,7 +17,11 @@
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -26,7 +30,7 @@
|
||||
** object with the encoded representation of the string, including
|
||||
** the NULL terminator.
|
||||
*/
|
||||
static int binarize(
|
||||
static int SQLITE_TCLAPI binarize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -54,7 +58,7 @@ static int binarize(
|
||||
** If <do-calls> is 0, then the calls to sqlite3_value_text() are not
|
||||
** actually made.
|
||||
*/
|
||||
static int test_value_overhead(
|
||||
static int SQLITE_TCLAPI test_value_overhead(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -118,7 +122,7 @@ static u8 name_to_enc(Tcl_Interp *interp, Tcl_Obj *pObj){
|
||||
** Usage: test_translate <string/blob> <from enc> <to enc> ?<transient>?
|
||||
**
|
||||
*/
|
||||
static int test_translate(
|
||||
static int SQLITE_TCLAPI test_translate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -182,7 +186,7 @@ static int test_translate(
|
||||
** translation. If there is a problem an assert() will fail.
|
||||
**/
|
||||
void sqlite3UtfSelfTest(void);
|
||||
static int test_translate_selftest(
|
||||
static int SQLITE_TCLAPI test_translate_selftest(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+62
-8
@@ -16,7 +16,11 @@
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */
|
||||
|
||||
@@ -215,7 +219,9 @@ static int writeListSync(CrashFile *pFile, int isCrash){
|
||||
}
|
||||
|
||||
#ifdef TRACE_CRASHTEST
|
||||
printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
|
||||
if( pFile ){
|
||||
printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
|
||||
}
|
||||
#endif
|
||||
|
||||
ppPtr = &g.pWriteList;
|
||||
@@ -701,6 +707,10 @@ static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
|
||||
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
|
||||
return pVfs->xCurrentTime(pVfs, pTimeOut);
|
||||
}
|
||||
static int cfGetLastError(sqlite3_vfs *pCfVfs, int n, char *z){
|
||||
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
|
||||
return pVfs->xGetLastError(pVfs, n, z);
|
||||
}
|
||||
|
||||
static int processDevSymArgs(
|
||||
Tcl_Interp *interp,
|
||||
@@ -795,13 +805,34 @@ static int processDevSymArgs(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_crash_now
|
||||
**
|
||||
** Simulate a crash immediately. This function does not return
|
||||
** (writeListSync() calls exit(-1)).
|
||||
*/
|
||||
static int SQLITE_TCLAPI crashNowCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
writeListSync(0, 1);
|
||||
assert( 0 );
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite_crash_enable ENABLE
|
||||
**
|
||||
** Parameter ENABLE must be a boolean value. If true, then the "crash"
|
||||
** vfs is added to the system. If false, it is removed.
|
||||
*/
|
||||
static int crashEnableCmd(
|
||||
static int SQLITE_TCLAPI crashEnableCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -827,7 +858,7 @@ static int crashEnableCmd(
|
||||
cfRandomness, /* xRandomness */
|
||||
cfSleep, /* xSleep */
|
||||
cfCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetlastError */
|
||||
cfGetLastError, /* xGetLastError */
|
||||
0, /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
@@ -876,7 +907,7 @@ static int crashEnableCmd(
|
||||
** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1
|
||||
**
|
||||
*/
|
||||
static int crashParamsObjCmd(
|
||||
static int SQLITE_TCLAPI crashParamsObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -923,7 +954,7 @@ error:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
static int devSymObjCmd(
|
||||
static int SQLITE_TCLAPI devSymObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -940,12 +971,33 @@ static int devSymObjCmd(
|
||||
devsym_register(iDc, iSectorSize);
|
||||
|
||||
return TCL_OK;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: unregister_devsim
|
||||
*/
|
||||
static int SQLITE_TCLAPI dsUnregisterObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void devsym_unregister(void);
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
devsym_unregister();
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: register_jt_vfs ?-default? PARENT-VFS
|
||||
*/
|
||||
static int jtObjCmd(
|
||||
static int SQLITE_TCLAPI jtObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -983,7 +1035,7 @@ static int jtObjCmd(
|
||||
/*
|
||||
** tclcmd: unregister_jt_vfs
|
||||
*/
|
||||
static int jtUnregisterObjCmd(
|
||||
static int SQLITE_TCLAPI jtUnregisterObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1009,7 +1061,9 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
|
||||
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, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
|
||||
#endif
|
||||
|
||||
+18
-14
@@ -13,7 +13,11 @@
|
||||
** Derived from test4.c.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
|
||||
@@ -149,7 +153,7 @@ static int parse_client_id(Tcl_Interp *interp, const char *zArg){
|
||||
** NAME should be an upper case letter. Start the thread running with
|
||||
** an open connection to the given database.
|
||||
*/
|
||||
static int tcl_client_create(
|
||||
static int SQLITE_TCLAPI tcl_client_create(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -202,7 +206,7 @@ static void client_wait(Thread *p){
|
||||
**
|
||||
** Wait on thread ID to reach its idle state.
|
||||
*/
|
||||
static int tcl_client_wait(
|
||||
static int SQLITE_TCLAPI tcl_client_wait(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -246,7 +250,7 @@ static void stop_thread(Thread *p){
|
||||
** Cause a client thread to shut itself down. Wait for the shutdown to be
|
||||
** completed. If ID is "*" then stop all client threads.
|
||||
*/
|
||||
static int tcl_client_halt(
|
||||
static int SQLITE_TCLAPI tcl_client_halt(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -294,7 +298,7 @@ static int tcl_client_halt(
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** number of columns in the result set.
|
||||
*/
|
||||
static int tcl_client_argc(
|
||||
static int SQLITE_TCLAPI tcl_client_argc(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -326,7 +330,7 @@ static int tcl_client_argc(
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** value of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_client_argv(
|
||||
static int SQLITE_TCLAPI tcl_client_argv(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -362,7 +366,7 @@ static int tcl_client_argv(
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** name of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_client_colname(
|
||||
static int SQLITE_TCLAPI tcl_client_colname(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -400,7 +404,7 @@ extern const char *sqlite3ErrName(int);
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** result code from that operation.
|
||||
*/
|
||||
static int tcl_client_result(
|
||||
static int SQLITE_TCLAPI tcl_client_result(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -432,7 +436,7 @@ static int tcl_client_result(
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** error string.
|
||||
*/
|
||||
static int tcl_client_error(
|
||||
static int SQLITE_TCLAPI tcl_client_error(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -477,7 +481,7 @@ static void do_compile(Thread *p){
|
||||
**
|
||||
** Compile a new virtual machine.
|
||||
*/
|
||||
static int tcl_client_compile(
|
||||
static int SQLITE_TCLAPI tcl_client_compile(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -530,7 +534,7 @@ static void do_step(Thread *p){
|
||||
**
|
||||
** Advance the virtual machine by one step
|
||||
*/
|
||||
static int tcl_client_step(
|
||||
static int SQLITE_TCLAPI tcl_client_step(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -572,7 +576,7 @@ static void do_finalize(Thread *p){
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_client_finalize(
|
||||
static int SQLITE_TCLAPI tcl_client_finalize(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -616,7 +620,7 @@ static void do_reset(Thread *p){
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_client_reset(
|
||||
static int SQLITE_TCLAPI tcl_client_reset(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
@@ -647,7 +651,7 @@ static int tcl_client_reset(
|
||||
**
|
||||
** Interchange the sqlite* pointer between two threads.
|
||||
*/
|
||||
static int tcl_client_swap(
|
||||
static int SQLITE_TCLAPI tcl_client_swap(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
|
||||
+7
-3
@@ -14,7 +14,11 @@
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -1353,7 +1357,7 @@ static void moduleDestroy(void *p){
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_echo_module(
|
||||
static int SQLITE_TCLAPI register_echo_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -1393,7 +1397,7 @@ static int register_echo_module(
|
||||
**
|
||||
** sqlite3_declare_vtab DB SQL
|
||||
*/
|
||||
static int declare_vtab(
|
||||
static int SQLITE_TCLAPI declare_vtab(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+8
-4
@@ -15,14 +15,18 @@
|
||||
** as there is not much point in binding to Tcl.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** c_collation_test
|
||||
*/
|
||||
static int c_collation_test(
|
||||
static int SQLITE_TCLAPI c_collation_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -63,7 +67,7 @@ error_out:
|
||||
/*
|
||||
** c_realloc_test
|
||||
*/
|
||||
static int c_realloc_test(
|
||||
static int SQLITE_TCLAPI c_realloc_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -104,7 +108,7 @@ error_out:
|
||||
/*
|
||||
** c_misuse_test
|
||||
*/
|
||||
static int c_misuse_test(
|
||||
static int SQLITE_TCLAPI c_misuse_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+13
-6
@@ -15,7 +15,14 @@
|
||||
*/
|
||||
|
||||
#define TCL_THREADS
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ASYNCIO
|
||||
|
||||
@@ -36,7 +43,7 @@ TCL_DECLARE_MUTEX(testasync_g_writerMutex);
|
||||
/*
|
||||
** sqlite3async_initialize PARENT-VFS ISDEFAULT
|
||||
*/
|
||||
static int testAsyncInit(
|
||||
static int SQLITE_TCLAPI testAsyncInit(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -69,7 +76,7 @@ static int testAsyncInit(
|
||||
/*
|
||||
** sqlite3async_shutdown
|
||||
*/
|
||||
static int testAsyncShutdown(
|
||||
static int SQLITE_TCLAPI testAsyncShutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -93,7 +100,7 @@ static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){
|
||||
**
|
||||
** Start a new writer thread.
|
||||
*/
|
||||
static int testAsyncStart(
|
||||
static int SQLITE_TCLAPI testAsyncStart(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -125,7 +132,7 @@ static int testAsyncStart(
|
||||
** If the current writer thread is set to run forever then this
|
||||
** command would block forever. To prevent that, an error is returned.
|
||||
*/
|
||||
static int testAsyncWait(
|
||||
static int SQLITE_TCLAPI testAsyncWait(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -151,7 +158,7 @@ static int testAsyncWait(
|
||||
/*
|
||||
** sqlite3async_control OPTION ?VALUE?
|
||||
*/
|
||||
static int testAsyncControl(
|
||||
static int SQLITE_TCLAPI testAsyncControl(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+21
-14
@@ -11,7 +11,14 @@
|
||||
*************************************************************************
|
||||
** Test extension for testing the sqlite3_auto_extension() function.
|
||||
*/
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "sqlite3ext.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
@@ -87,13 +94,13 @@ static int broken_init(
|
||||
**
|
||||
** Register the "sqr" extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtSqrObjCmd(
|
||||
static int SQLITE_TCLAPI autoExtSqrObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)sqr_init);
|
||||
int rc = sqlite3_auto_extension((void(*)(void))sqr_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -103,13 +110,13 @@ static int autoExtSqrObjCmd(
|
||||
**
|
||||
** Unregister the "sqr" extension.
|
||||
*/
|
||||
static int cancelAutoExtSqrObjCmd(
|
||||
static int SQLITE_TCLAPI cancelAutoExtSqrObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)sqr_init);
|
||||
int rc = sqlite3_cancel_auto_extension((void(*)(void))sqr_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -119,13 +126,13 @@ static int cancelAutoExtSqrObjCmd(
|
||||
**
|
||||
** Register the "cube" extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtCubeObjCmd(
|
||||
static int SQLITE_TCLAPI autoExtCubeObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)cube_init);
|
||||
int rc = sqlite3_auto_extension((void(*)(void))cube_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -135,13 +142,13 @@ static int autoExtCubeObjCmd(
|
||||
**
|
||||
** Unregister the "cube" extension.
|
||||
*/
|
||||
static int cancelAutoExtCubeObjCmd(
|
||||
static int SQLITE_TCLAPI cancelAutoExtCubeObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)cube_init);
|
||||
int rc = sqlite3_cancel_auto_extension((void(*)(void))cube_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -151,13 +158,13 @@ static int cancelAutoExtCubeObjCmd(
|
||||
**
|
||||
** Register the broken extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtBrokenObjCmd(
|
||||
static int SQLITE_TCLAPI autoExtBrokenObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)broken_init);
|
||||
int rc = sqlite3_auto_extension((void(*)(void))broken_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -167,13 +174,13 @@ static int autoExtBrokenObjCmd(
|
||||
**
|
||||
** Unregister the broken extension.
|
||||
*/
|
||||
static int cancelAutoExtBrokenObjCmd(
|
||||
static int SQLITE_TCLAPI cancelAutoExtBrokenObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)broken_init);
|
||||
int rc = sqlite3_cancel_auto_extension((void(*)(void))broken_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -186,7 +193,7 @@ static int cancelAutoExtBrokenObjCmd(
|
||||
**
|
||||
** Reset all auto-extensions
|
||||
*/
|
||||
static int resetAutoExtObjCmd(
|
||||
static int SQLITE_TCLAPI resetAutoExtObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+11
-4
@@ -13,7 +13,14 @@
|
||||
**
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
#include <assert.h>
|
||||
|
||||
@@ -23,7 +30,7 @@ extern const char *sqlite3ErrName(int);
|
||||
/* These functions are implemented in test1.c. */
|
||||
extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
|
||||
|
||||
static int backupTestCmd(
|
||||
static int SQLITE_TCLAPI backupTestCmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -98,7 +105,7 @@ static int backupTestCmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void backupTestFinish(ClientData clientData){
|
||||
static void SQLITE_TCLAPI backupTestFinish(ClientData clientData){
|
||||
sqlite3_backup *pBackup = (sqlite3_backup *)clientData;
|
||||
sqlite3_backup_finish(pBackup);
|
||||
}
|
||||
@@ -107,7 +114,7 @@ static void backupTestFinish(ClientData clientData){
|
||||
** sqlite3_backup CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME
|
||||
**
|
||||
*/
|
||||
static int backupTestInit(
|
||||
static int SQLITE_TCLAPI backupTestInit(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
** 2016-03-01
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table xBestIndex method and the query
|
||||
** planner.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** INSTRUCTIONS
|
||||
**
|
||||
** This module exports a single tcl command - [register_tcl_module]. When
|
||||
** invoked, it registers a special virtual table module with a database
|
||||
** connection.
|
||||
**
|
||||
** The virtual table is currently read-only. And always returns zero rows.
|
||||
** It is created with a single argument - the name of a Tcl command - as
|
||||
** follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
|
||||
**
|
||||
** The command [tcl_command] is invoked when the table is first created (or
|
||||
** connected), when the xBestIndex() method is invoked and when the xFilter()
|
||||
** method is called. When it is created (or connected), it is invoked as
|
||||
** follows:
|
||||
**
|
||||
** tcl_command xConnect
|
||||
**
|
||||
** In this case the return value of the script is passed to the
|
||||
** sqlite3_declare_vtab() function to create the virtual table schema.
|
||||
**
|
||||
** When the xBestIndex() method is called by SQLite, the Tcl command is
|
||||
** invoked as:
|
||||
**
|
||||
** tcl_command xBestIndex CONSTRAINTS ORDERBY MASK
|
||||
**
|
||||
** where CONSTRAINTS is a tcl representation of the aConstraints[] array,
|
||||
** ORDERBY is a representation of the contents of the aOrderBy[] array and
|
||||
** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual
|
||||
** table is declared as:
|
||||
**
|
||||
** CREATE TABLE x1(a, b, c)
|
||||
**
|
||||
** and the query is:
|
||||
**
|
||||
** SELECT * FROM x1 WHERE a=? AND c<? ORDER BY b, c;
|
||||
**
|
||||
** then the Tcl command is:
|
||||
**
|
||||
** tcl_command xBestIndex \
|
||||
** {{op eq column 0 usable 1} {op lt column 2 usable 1}} \
|
||||
** {{column 1 desc 0} {column 2 desc 0}} \
|
||||
** 7
|
||||
**
|
||||
** The return value of the script is a list of key-value pairs used to
|
||||
** populate the output fields of the sqlite3_index_info structure. Possible
|
||||
** keys and the usage of the accompanying values are:
|
||||
**
|
||||
** "orderby" (value of orderByConsumed flag)
|
||||
** "cost" (value of estimatedCost field)
|
||||
** "rows" (value of estimatedRows field)
|
||||
** "use" (index of used constraint in aConstraint[])
|
||||
** "omit" (like "use", but also sets omit flag)
|
||||
** "idxnum" (value of idxNum field)
|
||||
** "idxstr" (value of idxStr field)
|
||||
**
|
||||
** Refer to code below for further details.
|
||||
**
|
||||
** When SQLite calls the xFilter() method, this module invokes the following
|
||||
** Tcl script:
|
||||
**
|
||||
** tcl_command xFilter IDXNUM IDXSTR ARGLIST
|
||||
**
|
||||
** IDXNUM and IDXSTR are the values of the idxNum and idxStr parameters
|
||||
** passed to xFilter. ARGLIST is a Tcl list containing each of the arguments
|
||||
** passed to xFilter in text form.
|
||||
**
|
||||
** As with xBestIndex(), the return value of the script is interpreted as a
|
||||
** list of key-value pairs. There is currently only one key defined - "sql".
|
||||
** The value must be the full text of an SQL statement that returns the data
|
||||
** for the current scan. The leftmost column returned by the SELECT is assumed
|
||||
** to contain the rowid. Other columns must follow, in order from left to
|
||||
** right.
|
||||
*/
|
||||
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct tcl_vtab tcl_vtab;
|
||||
typedef struct tcl_cursor tcl_cursor;
|
||||
|
||||
/*
|
||||
** A fs virtual-table object
|
||||
*/
|
||||
struct tcl_vtab {
|
||||
sqlite3_vtab base;
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pCmd;
|
||||
sqlite3 *db;
|
||||
};
|
||||
|
||||
/* A tcl cursor object */
|
||||
struct tcl_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pStmt; /* Read data from here */
|
||||
};
|
||||
|
||||
/*
|
||||
** Dequote string z in place.
|
||||
*/
|
||||
static void tclDequote(char *z){
|
||||
char q = z[0];
|
||||
|
||||
/* Set stack variable q to the close-quote character */
|
||||
if( q=='[' || q=='\'' || q=='"' || q=='`' ){
|
||||
int iIn = 1;
|
||||
int iOut = 0;
|
||||
if( q=='[' ) q = ']';
|
||||
|
||||
while( ALWAYS(z[iIn]) ){
|
||||
if( z[iIn]==q ){
|
||||
if( z[iIn+1]!=q ){
|
||||
/* Character iIn was the close quote. */
|
||||
iIn++;
|
||||
break;
|
||||
}else{
|
||||
/* Character iIn and iIn+1 form an escaped quote character. Skip
|
||||
** the input cursor past both and copy a single quote character
|
||||
** to the output buffer. */
|
||||
iIn += 2;
|
||||
z[iOut++] = q;
|
||||
}
|
||||
}else{
|
||||
z[iOut++] = z[iIn++];
|
||||
}
|
||||
}
|
||||
|
||||
z[iOut] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the implementation of both the xConnect and xCreate
|
||||
** methods of the fs virtual table.
|
||||
**
|
||||
** The argv[] array contains the following:
|
||||
**
|
||||
** argv[0] -> module name ("fs")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[...] -> other module argument fields.
|
||||
*/
|
||||
static int tclConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
Tcl_Interp *interp = (Tcl_Interp*)pAux;
|
||||
tcl_vtab *pTab = 0;
|
||||
char *zCmd = 0;
|
||||
Tcl_Obj *pScript = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( argc!=4 ){
|
||||
*pzErr = sqlite3_mprintf("wrong number of arguments");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
zCmd = sqlite3_malloc64(strlen(argv[3])+1);
|
||||
pTab = (tcl_vtab*)sqlite3_malloc64(sizeof(tcl_vtab));
|
||||
if( zCmd && pTab ){
|
||||
memcpy(zCmd, argv[3], strlen(argv[3])+1);
|
||||
tclDequote(zCmd);
|
||||
memset(pTab, 0, sizeof(tcl_vtab));
|
||||
|
||||
pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
|
||||
pTab->interp = interp;
|
||||
pTab->db = db;
|
||||
Tcl_IncrRefCount(pTab->pCmd);
|
||||
|
||||
pScript = Tcl_DuplicateObj(pTab->pCmd);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
|
||||
|
||||
rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ){
|
||||
*pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pTab);
|
||||
pTab = 0;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
sqlite3_free(zCmd);
|
||||
*ppVtab = &pTab->base;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
static int tclDisconnect(sqlite3_vtab *pVtab){
|
||||
tcl_vtab *pTab = (tcl_vtab*)pVtab;
|
||||
Tcl_DecrRefCount(pTab->pCmd);
|
||||
sqlite3_free(pTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new tcl cursor.
|
||||
*/
|
||||
static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
tcl_cursor *pCur;
|
||||
pCur = sqlite3_malloc(sizeof(tcl_cursor));
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(tcl_cursor));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tcl cursor.
|
||||
*/
|
||||
static int tclClose(sqlite3_vtab_cursor *cur){
|
||||
tcl_cursor *pCur = (tcl_cursor *)cur;
|
||||
if( pCur ){
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
sqlite3_free(pCur);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclNext(sqlite3_vtab_cursor *pVtabCursor){
|
||||
tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
|
||||
if( pCsr->pStmt ){
|
||||
tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
|
||||
int rc = sqlite3_step(pCsr->pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
const char *zErr;
|
||||
rc = sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
if( rc!=SQLITE_OK ){
|
||||
zErr = sqlite3_errmsg(pTab->db);
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
|
||||
tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
|
||||
Tcl_Interp *interp = pTab->interp;
|
||||
Tcl_Obj *pScript;
|
||||
Tcl_Obj *pArg;
|
||||
int ii;
|
||||
int rc;
|
||||
|
||||
pScript = Tcl_DuplicateObj(pTab->pCmd);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xFilter", -1));
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(idxNum));
|
||||
if( idxStr ){
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(idxStr, -1));
|
||||
}else{
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("", -1));
|
||||
}
|
||||
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
for(ii=0; ii<argc; ii++){
|
||||
const char *zVal = (const char*)sqlite3_value_text(argv[ii]);
|
||||
Tcl_Obj *pVal;
|
||||
if( zVal==0 ){
|
||||
pVal = Tcl_NewObj();
|
||||
}else{
|
||||
pVal = Tcl_NewStringObj(zVal, -1);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pArg, pVal);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pScript, pArg);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
|
||||
rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}else{
|
||||
/* Analyze the scripts return value. The return value should be a tcl
|
||||
** list object with an even number of elements. The first element of each
|
||||
** pair must be one of:
|
||||
**
|
||||
** "sql" (SQL statement to return data)
|
||||
*/
|
||||
Tcl_Obj *pRes = Tcl_GetObjResult(interp);
|
||||
Tcl_Obj **apElem = 0;
|
||||
int nElem;
|
||||
rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
|
||||
if( rc!=TCL_OK ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}else{
|
||||
for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
|
||||
const char *zCmd = Tcl_GetString(apElem[ii]);
|
||||
Tcl_Obj *p = apElem[ii+1];
|
||||
if( sqlite3_stricmp("sql", zCmd)==0 ){
|
||||
const char *zSql = Tcl_GetString(p);
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
const char *zErr = sqlite3_errmsg(pTab->db);
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zErr);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = tclNext(pVtabCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tclColumn(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
|
||||
tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
|
||||
*pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclEof(sqlite3_vtab_cursor *pVtabCursor){
|
||||
tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
|
||||
return (pCsr->pStmt==0);
|
||||
}
|
||||
|
||||
static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
tcl_vtab *pTab = (tcl_vtab*)tab;
|
||||
Tcl_Interp *interp = pTab->interp;
|
||||
Tcl_Obj *pArg;
|
||||
Tcl_Obj *pScript;
|
||||
int ii;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pScript = Tcl_DuplicateObj(pTab->pCmd);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
|
||||
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
Tcl_Obj *pElem = Tcl_NewObj();
|
||||
const char *zOp = "?";
|
||||
|
||||
Tcl_IncrRefCount(pElem);
|
||||
|
||||
switch( pCons->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||
zOp = "eq"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GT:
|
||||
zOp = "gt"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LE:
|
||||
zOp = "le"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT:
|
||||
zOp = "lt"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GE:
|
||||
zOp = "ge"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_MATCH:
|
||||
zOp = "match"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIKE:
|
||||
zOp = "like"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GLOB:
|
||||
zOp = "glob"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_REGEXP:
|
||||
zOp = "regexp"; break;
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pArg, pElem);
|
||||
Tcl_DecrRefCount(pElem);
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, pArg);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
|
||||
struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
|
||||
Tcl_Obj *pElem = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pElem);
|
||||
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pArg, pElem);
|
||||
Tcl_DecrRefCount(pElem);
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, pArg);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
|
||||
|
||||
rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
|
||||
Tcl_DecrRefCount(pScript);
|
||||
if( rc!=TCL_OK ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}else{
|
||||
/* Analyze the scripts return value. The return value should be a tcl
|
||||
** list object with an even number of elements. The first element of each
|
||||
** pair must be one of:
|
||||
**
|
||||
** "orderby" (value of orderByConsumed flag)
|
||||
** "cost" (value of estimatedCost field)
|
||||
** "rows" (value of estimatedRows field)
|
||||
** "use" (index of used constraint in aConstraint[])
|
||||
** "idxnum" (value of idxNum field)
|
||||
** "idxstr" (value of idxStr field)
|
||||
** "omit" (index of omitted constraint in aConstraint[])
|
||||
*/
|
||||
Tcl_Obj *pRes = Tcl_GetObjResult(interp);
|
||||
Tcl_Obj **apElem = 0;
|
||||
int nElem;
|
||||
rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
|
||||
if( rc!=TCL_OK ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}else{
|
||||
int iArgv = 1;
|
||||
for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
|
||||
const char *zCmd = Tcl_GetString(apElem[ii]);
|
||||
Tcl_Obj *p = apElem[ii+1];
|
||||
if( sqlite3_stricmp("cost", zCmd)==0 ){
|
||||
rc = Tcl_GetDoubleFromObj(interp, p, &pIdxInfo->estimatedCost);
|
||||
}else
|
||||
if( sqlite3_stricmp("orderby", zCmd)==0 ){
|
||||
rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed);
|
||||
}else
|
||||
if( sqlite3_stricmp("idxnum", zCmd)==0 ){
|
||||
rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum);
|
||||
}else
|
||||
if( sqlite3_stricmp("idxstr", zCmd)==0 ){
|
||||
sqlite3_free(pIdxInfo->idxStr);
|
||||
pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p));
|
||||
pIdxInfo->needToFreeIdxStr = 1;
|
||||
}else
|
||||
if( sqlite3_stricmp("rows", zCmd)==0 ){
|
||||
Tcl_WideInt x = 0;
|
||||
rc = Tcl_GetWideIntFromObj(interp, p, &x);
|
||||
pIdxInfo->estimatedRows = (tRowcnt)x;
|
||||
}else
|
||||
if( sqlite3_stricmp("use", zCmd)==0
|
||||
|| sqlite3_stricmp("omit", zCmd)==0
|
||||
){
|
||||
int iCons;
|
||||
rc = Tcl_GetIntFromObj(interp, p, &iCons);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons);
|
||||
}else{
|
||||
int bOmit = (zCmd[0]=='o' || zCmd[0]=='O');
|
||||
pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++;
|
||||
pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
|
||||
}
|
||||
if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module tclModule = {
|
||||
0, /* iVersion */
|
||||
tclConnect,
|
||||
tclConnect,
|
||||
tclBestIndex,
|
||||
tclDisconnect,
|
||||
tclDisconnect,
|
||||
tclOpen, /* xOpen - open a cursor */
|
||||
tclClose, /* xClose - close a cursor */
|
||||
tclFilter, /* xFilter - configure scan constraints */
|
||||
tclNext, /* xNext - advance a cursor */
|
||||
tclEof, /* xEof - check for end of scan */
|
||||
tclColumn, /* xColumn - read data */
|
||||
tclRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int SQLITE_TCLAPI register_tcl_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "tcl", &tclModule, (void *)interp);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetesttcl_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_tcl_module", register_tcl_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
+11
-7
@@ -12,7 +12,11 @@
|
||||
**
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -95,7 +99,7 @@ static char *blobStringFromObj(Tcl_Obj *pObj){
|
||||
**
|
||||
** Tcl test harness for the sqlite3_blob_open() function.
|
||||
*/
|
||||
static int test_blob_open(
|
||||
static int SQLITE_TCLAPI test_blob_open(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* Calling TCL interpreter */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -105,7 +109,7 @@ static int test_blob_open(
|
||||
const char *zDb;
|
||||
const char *zTable;
|
||||
const char *zColumn;
|
||||
sqlite_int64 iRowid;
|
||||
Tcl_WideInt iRowid;
|
||||
int flags;
|
||||
const char *zVarname;
|
||||
int nVarname;
|
||||
@@ -146,7 +150,7 @@ static int test_blob_open(
|
||||
/*
|
||||
** sqlite3_blob_close HANDLE
|
||||
*/
|
||||
static int test_blob_close(
|
||||
static int SQLITE_TCLAPI test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -174,7 +178,7 @@ static int test_blob_close(
|
||||
/*
|
||||
** sqlite3_blob_bytes HANDLE
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
static int SQLITE_TCLAPI test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -210,7 +214,7 @@ static int test_blob_bytes(
|
||||
** text representation of the returned error code (i.e. "SQLITE_NOMEM")
|
||||
** and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_read(
|
||||
static int SQLITE_TCLAPI test_blob_read(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -262,7 +266,7 @@ static int test_blob_read(
|
||||
** result is set to the text representation of the returned error code
|
||||
** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_write(
|
||||
static int SQLITE_TCLAPI test_blob_write(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+6
-2
@@ -14,7 +14,11 @@
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
#include "btreeInt.h"
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_shared_cache_report
|
||||
@@ -22,7 +26,7 @@
|
||||
** Return a list of file that are shared and the number of
|
||||
** references to each file.
|
||||
*/
|
||||
int sqlite3BtreeSharedCacheReport(
|
||||
int SQLITE_TCLAPI sqlite3BtreeSharedCacheReport(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+35
-8
@@ -24,7 +24,11 @@
|
||||
# include "os_win.h"
|
||||
#endif
|
||||
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -79,6 +83,13 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "debug", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "default_ckptfullfsync",
|
||||
SQLITE_DEFAULT_CKPTFULLFSYNC ? "1" : "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "default_ckptfullfsync", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
Tcl_SetVar2(interp, "sqlite_options", "direct_read", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -97,6 +108,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "lfs", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "pagecache_overflow_stats","0",TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "pagecache_overflow_stats","1",TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mmap", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -143,6 +160,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
Tcl_SetVar2(interp, "sqlite_options", "snapshot", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -370,12 +393,6 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts5", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -527,6 +544,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_SESSION
|
||||
Tcl_SetVar2(interp, "sqlite_options", "session", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "session", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -582,7 +605,11 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
Tcl_SetVar2(interp, "sqlite_options", "threadsafe",
|
||||
STRINGVALUE(SQLITE_THREADSAFE), TCL_GLOBAL_ONLY);
|
||||
SQLITE_THREADSAFE ? "1" : "0", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "threadsafe1",
|
||||
SQLITE_THREADSAFE==1 ? "1" : "0", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "threadsafe2",
|
||||
SQLITE_THREADSAFE==2 ? "1" : "0", TCL_GLOBAL_ONLY);
|
||||
assert( sqlite3_threadsafe()==SQLITE_THREADSAFE );
|
||||
|
||||
#ifdef SQLITE_OMIT_TEMPDB
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
** 2016 September 10
|
||||
**
|
||||
** 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 test code to delete an SQLite database and all
|
||||
** of its associated files. Associated files include:
|
||||
**
|
||||
** * The journal file.
|
||||
** * The wal file.
|
||||
** * The SQLITE_ENABLE_8_3_NAMES version of the db, journal or wal files.
|
||||
** * Files created by the test_multiplex.c module to extend any of the
|
||||
** above.
|
||||
*/
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
# include <io.h>
|
||||
# define F_OK 0
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/* The following #defines are copied from test_multiplex.c */
|
||||
#ifndef MX_CHUNK_NUMBER
|
||||
# define MX_CHUNK_NUMBER 299
|
||||
#endif
|
||||
#ifndef SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET
|
||||
# define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
|
||||
#endif
|
||||
#ifndef SQLITE_MULTIPLEX_WAL_8_3_OFFSET
|
||||
# define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine is a copy of (most of) the code from SQLite function
|
||||
** sqlite3FileSuffix3(). It modifies the filename in buffer z in the
|
||||
** same way as SQLite does when in 8.3 filenames mode.
|
||||
*/
|
||||
static void sqlite3Delete83Name(char *z){
|
||||
int i, sz;
|
||||
sz = strlen(z);
|
||||
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
||||
if( z[i]=='.' && (sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
|
||||
}
|
||||
|
||||
/*
|
||||
** zFile is a filename. Assuming no error occurs, if this file exists,
|
||||
** set *pbExists to true and unlink it. Or, if the file does not exist,
|
||||
** set *pbExists to false before returning.
|
||||
**
|
||||
** If an error occurs, the value of errno is returned. Or, if no error
|
||||
** occurs, zero is returned.
|
||||
*/
|
||||
static int sqlite3DeleteUnlinkIfExists(const char *zFile, int *pbExists){
|
||||
int rc;
|
||||
rc = access(zFile, F_OK);
|
||||
if( rc ){
|
||||
if( errno==ENOENT ){
|
||||
if( pbExists ) *pbExists = 0;
|
||||
return 0;
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
if( pbExists ) *pbExists = 1;
|
||||
rc = unlink(zFile);
|
||||
if( rc ) return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the database file identified by the string argument passed to this
|
||||
** function. The string must contain a filename, not an SQLite URI.
|
||||
*/
|
||||
int sqlite3_delete_database(
|
||||
const char *zFile /* File to delete */
|
||||
){
|
||||
char *zBuf; /* Buffer to sprintf() filenames to */
|
||||
int nBuf; /* Size of buffer in bytes */
|
||||
int rc = 0; /* System error code */
|
||||
int i; /* Iterate through azFmt[] and aMFile[] */
|
||||
|
||||
const char *azFmt[] = { "%s", "%s-journal", "%s-wal", "%s-shm" };
|
||||
|
||||
struct MFile {
|
||||
const char *zFmt;
|
||||
int iOffset;
|
||||
int b83;
|
||||
} aMFile[] = {
|
||||
{ "%s%03d", 0, 0 },
|
||||
{ "%s-journal%03d", 0, 0 },
|
||||
{ "%s-wal%03d", 0, 0 },
|
||||
{ "%s%03d", 0, 1 },
|
||||
{ "%s-journal%03d", SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET, 1 },
|
||||
{ "%s-wal%03d", SQLITE_MULTIPLEX_WAL_8_3_OFFSET, 1 },
|
||||
};
|
||||
|
||||
/* Allocate a buffer large enough for any of the files that need to be
|
||||
** deleted. */
|
||||
nBuf = strlen(zFile) + 100;
|
||||
zBuf = (char*)sqlite3_malloc(nBuf);
|
||||
if( zBuf==0 ) return SQLITE_NOMEM;
|
||||
|
||||
/* Delete both the regular and 8.3 filenames versions of the database,
|
||||
** journal, wal and shm files. */
|
||||
for(i=0; rc==0 && i<sizeof(azFmt)/sizeof(azFmt[0]); i++){
|
||||
sqlite3_snprintf(nBuf, zBuf, azFmt[i], zFile);
|
||||
rc = sqlite3DeleteUnlinkIfExists(zBuf, 0);
|
||||
if( rc==0 && i!=0 ){
|
||||
sqlite3Delete83Name(zBuf);
|
||||
rc = sqlite3DeleteUnlinkIfExists(zBuf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete any multiplexor files */
|
||||
for(i=0; rc==0 && i<sizeof(aMFile)/sizeof(aMFile[0]); i++){
|
||||
struct MFile *p = &aMFile[i];
|
||||
int iChunk;
|
||||
for(iChunk=1; iChunk<=MX_CHUNK_NUMBER; iChunk++){
|
||||
int bExists;
|
||||
sqlite3_snprintf(nBuf, zBuf, p->zFmt, zFile, iChunk+p->iOffset);
|
||||
if( p->b83 ) sqlite3Delete83Name(zBuf);
|
||||
rc = sqlite3DeleteUnlinkIfExists(zBuf, &bExists);
|
||||
if( bExists==0 || rc!=0 ) break;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(zBuf);
|
||||
return (rc ? SQLITE_ERROR : SQLITE_OK);
|
||||
}
|
||||
+10
-3
@@ -641,10 +641,17 @@ sqlite3_vfs *sqlite3_demovfs(void){
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
static int register_demovfs(
|
||||
static int SQLITE_TCLAPI register_demovfs(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -653,7 +660,7 @@ static int register_demovfs(
|
||||
sqlite3_vfs_register(sqlite3_demovfs(), 1);
|
||||
return TCL_OK;
|
||||
}
|
||||
static int unregister_demovfs(
|
||||
static int SQLITE_TCLAPI unregister_demovfs(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+9
-1
@@ -133,7 +133,8 @@ struct DevsymGlobal g = {0, 0, 512};
|
||||
*/
|
||||
static int devsymClose(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsClose(p->pReal);
|
||||
sqlite3OsClose(p->pReal);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -395,4 +396,11 @@ void devsym_register(int iDeviceChar, int iSectorSize){
|
||||
}
|
||||
}
|
||||
|
||||
void devsym_unregister(){
|
||||
sqlite3_vfs_unregister(&devsym_vfs);
|
||||
g.pVfs = 0;
|
||||
g.iDeviceChar = 0;
|
||||
g.iSectorSize = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+14
-6
@@ -62,7 +62,11 @@
|
||||
** SELECT * FROM fstree WHERE path LIKE '/home/dan/sqlite/%'
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -495,10 +499,14 @@ static int fstreeFilter(
|
||||
char aWild[2] = { '\0', '\0' };
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
zRoot = sqlite3_mprintf("%s%c", getenv("SystemDrive"), '/');
|
||||
nRoot = strlen(zRoot);
|
||||
zPrefix = sqlite3_mprintf("%s", getenv("SystemDrive"));
|
||||
nPrefix = strlen(zPrefix);
|
||||
const char *zDrive = windirent_getenv("fstreeDrive");
|
||||
if( zDrive==0 ){
|
||||
zDrive = windirent_getenv("SystemDrive");
|
||||
}
|
||||
zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
|
||||
nRoot = sqlite3Strlen30(zRoot);
|
||||
zPrefix = sqlite3_mprintf("%s", zDrive);
|
||||
nPrefix = sqlite3Strlen30(zPrefix);
|
||||
#else
|
||||
zRoot = "/";
|
||||
nRoot = 1;
|
||||
@@ -871,7 +879,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_fs_module(
|
||||
static int SQLITE_TCLAPI register_fs_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+19
-11
@@ -13,7 +13,11 @@
|
||||
** implements new SQL functions used by the test scripts.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -21,7 +25,6 @@
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
** allocation fails, call sqlite3_result_error_nomem() to notify
|
||||
@@ -640,7 +643,11 @@ static void test_setsubtype(
|
||||
sqlite3_result_subtype(context, (unsigned int)sqlite3_value_int(argv[1]));
|
||||
}
|
||||
|
||||
static int registerTestFunctions(sqlite3 *db){
|
||||
static int registerTestFunctions(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pThunk
|
||||
){
|
||||
static const struct {
|
||||
char *zName;
|
||||
signed char nArg;
|
||||
@@ -689,16 +696,16 @@ static int registerTestFunctions(sqlite3 *db){
|
||||
** the standard set of test functions to be loaded into each new
|
||||
** database connection.
|
||||
*/
|
||||
static int autoinstall_test_funcs(
|
||||
static int SQLITE_TCLAPI autoinstall_test_funcs(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int Md5_Register(sqlite3*);
|
||||
int rc = sqlite3_auto_extension((void*)registerTestFunctions);
|
||||
extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int rc = sqlite3_auto_extension((void(*)(void))registerTestFunctions);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension((void*)Md5_Register);
|
||||
rc = sqlite3_auto_extension((void(*)(void))Md5_Register);
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
@@ -717,7 +724,7 @@ static void tFinal(sqlite3_context *a){}
|
||||
** Make various calls to sqlite3_create_function that do not have valid
|
||||
** parameters. Verify that the error condition is detected and reported.
|
||||
*/
|
||||
static int abuse_create_function(
|
||||
static int SQLITE_TCLAPI abuse_create_function(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -783,6 +790,7 @@ abuse_err:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
@@ -795,13 +803,13 @@ int Sqlitetest_func_Init(Tcl_Interp *interp){
|
||||
{ "abuse_create_function", abuse_create_function },
|
||||
};
|
||||
int i;
|
||||
extern int Md5_Register(sqlite3*);
|
||||
extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
|
||||
}
|
||||
sqlite3_initialize();
|
||||
sqlite3_auto_extension((void*)registerTestFunctions);
|
||||
sqlite3_auto_extension((void*)Md5_Register);
|
||||
sqlite3_auto_extension((void(*)(void))registerTestFunctions);
|
||||
sqlite3_auto_extension((void(*)(void))Md5_Register);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
+12
-8
@@ -18,7 +18,11 @@
|
||||
** easier and safer to build our own mechanism.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -94,7 +98,7 @@ int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){
|
||||
** beginning of the file. Convert that information to hexadecimal
|
||||
** and return the resulting HEX string.
|
||||
*/
|
||||
static int hexio_read(
|
||||
static int SQLITE_TCLAPI hexio_read(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -144,7 +148,7 @@ static int hexio_read(
|
||||
** Write DATA into file FILENAME beginning at OFFSET from the
|
||||
** beginning of the file. DATA is expressed in hexadecimal.
|
||||
*/
|
||||
static int hexio_write(
|
||||
static int SQLITE_TCLAPI hexio_write(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -192,7 +196,7 @@ static int hexio_write(
|
||||
** the value of that integer. HEXDATA can contain between 2 and 8
|
||||
** hexadecimal digits.
|
||||
*/
|
||||
static int hexio_get_int(
|
||||
static int SQLITE_TCLAPI hexio_get_int(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -232,7 +236,7 @@ static int hexio_get_int(
|
||||
**
|
||||
** Render INTEGER has a 16-bit big-endian integer in hexadecimal.
|
||||
*/
|
||||
static int hexio_render_int16(
|
||||
static int SQLITE_TCLAPI hexio_render_int16(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -259,7 +263,7 @@ static int hexio_render_int16(
|
||||
**
|
||||
** Render INTEGER has a 32-bit big-endian integer in hexadecimal.
|
||||
*/
|
||||
static int hexio_render_int32(
|
||||
static int SQLITE_TCLAPI hexio_render_int32(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -289,7 +293,7 @@ static int hexio_render_int32(
|
||||
** The UTF8 might not be well-formed. Run this string through
|
||||
** sqlite3Utf8to8() convert it back to hex and return the result.
|
||||
*/
|
||||
static int utf8_to_utf8(
|
||||
static int SQLITE_TCLAPI utf8_to_utf8(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -340,7 +344,7 @@ static int getFts3Varint(const char *p, sqlite_int64 *v){
|
||||
** Read a varint from the start of BLOB. Set variable VARNAME to contain
|
||||
** the interpreted value. Return the number of bytes of BLOB consumed.
|
||||
*/
|
||||
static int read_fts3varint(
|
||||
static int SQLITE_TCLAPI read_fts3varint(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+9
-5
@@ -27,7 +27,11 @@
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include <string.h>
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
static struct Wrapped {
|
||||
sqlite3_pcache_methods2 pcache;
|
||||
@@ -184,7 +188,7 @@ static void installInitWrappers(void){
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcachemethods);
|
||||
}
|
||||
|
||||
static int init_wrapper_install(
|
||||
static int SQLITE_TCLAPI init_wrapper_install(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -208,7 +212,7 @@ static int init_wrapper_install(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_uninstall(
|
||||
static int SQLITE_TCLAPI init_wrapper_uninstall(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -226,7 +230,7 @@ static int init_wrapper_uninstall(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_clear(
|
||||
static int SQLITE_TCLAPI init_wrapper_clear(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -243,7 +247,7 @@ static int init_wrapper_clear(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_query(
|
||||
static int SQLITE_TCLAPI init_wrapper_query(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+10
-3
@@ -270,7 +270,14 @@ SQLITE_API int sqlite3_intarray_bind(
|
||||
** Everything below is interface for testing this module.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Routines to encode and decode pointers
|
||||
@@ -286,7 +293,7 @@ extern const char *sqlite3ErrName(int);
|
||||
** Invoke the sqlite3_intarray_create interface. A string that becomes
|
||||
** the first parameter to sqlite3_intarray_bind.
|
||||
*/
|
||||
static int test_intarray_create(
|
||||
static int SQLITE_TCLAPI test_intarray_create(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
@@ -322,7 +329,7 @@ static int test_intarray_create(
|
||||
**
|
||||
** Invoke the sqlite3_intarray_bind interface on the given array of integers.
|
||||
*/
|
||||
static int test_intarray_bind(
|
||||
static int SQLITE_TCLAPI test_intarray_bind(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+3
-3
@@ -75,8 +75,8 @@
|
||||
** action to free the intarray objects.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#ifndef _INTARRAY_H_
|
||||
#define _INTARRAY_H_
|
||||
#ifndef SQLITE_INTARRAY_H
|
||||
#define SQLITE_INTARRAY_H
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
@@ -125,4 +125,4 @@ SQLITE_API int sqlite3_intarray_bind(
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _INTARRAY_H_ */
|
||||
#endif /* SQLITE_INTARRAY_H */
|
||||
|
||||
+18
-12
@@ -160,6 +160,7 @@ static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int jtSleep(sqlite3_vfs*, int microseconds);
|
||||
static int jtCurrentTime(sqlite3_vfs*, double*);
|
||||
static int jtCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
static int jtGetLastError(sqlite3_vfs*, int, char*);
|
||||
|
||||
static sqlite3_vfs jt_vfs = {
|
||||
2, /* iVersion */
|
||||
@@ -179,7 +180,7 @@ static sqlite3_vfs jt_vfs = {
|
||||
jtRandomness, /* xRandomness */
|
||||
jtSleep, /* xSleep */
|
||||
jtCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
jtGetLastError, /* xGetLastError */
|
||||
jtCurrentTimeInt64 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
@@ -256,7 +257,8 @@ static int jtClose(sqlite3_file *pFile){
|
||||
*pp = p->pNext;
|
||||
}
|
||||
leaveJtMutex();
|
||||
return sqlite3OsClose(p->pReal);
|
||||
sqlite3OsClose(p->pReal);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -284,9 +286,10 @@ static int jtRead(
|
||||
** b) The file-name specified when the file was opened matches
|
||||
** all but the final 8 characters of the journal file name.
|
||||
**
|
||||
** c) There is currently a reserved lock on the file.
|
||||
** c) There is currently a reserved lock on the file. This
|
||||
** condition is waived if the noLock argument is non-zero.
|
||||
**/
|
||||
static jt_file *locateDatabaseHandle(const char *zJournal){
|
||||
static jt_file *locateDatabaseHandle(const char *zJournal, int noLock){
|
||||
jt_file *pMain = 0;
|
||||
enterJtMutex();
|
||||
for(pMain=g.pList; pMain; pMain=pMain->pNext){
|
||||
@@ -294,7 +297,7 @@ static jt_file *locateDatabaseHandle(const char *zJournal){
|
||||
if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
|
||||
&& ((int)strlen(pMain->zName)==nName)
|
||||
&& 0==memcmp(pMain->zName, zJournal, nName)
|
||||
&& (pMain->eLock>=SQLITE_LOCK_RESERVED)
|
||||
&& ((pMain->eLock>=SQLITE_LOCK_RESERVED) || noLock)
|
||||
){
|
||||
break;
|
||||
}
|
||||
@@ -516,7 +519,7 @@ static int jtWrite(
|
||||
jt_file *p = (jt_file *)pFile;
|
||||
if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
|
||||
if( iOfst==0 ){
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName);
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName, 0);
|
||||
assert( pMain );
|
||||
|
||||
if( iAmt==28 ){
|
||||
@@ -561,7 +564,7 @@ static int jtWrite(
|
||||
|
||||
rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
|
||||
if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName);
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName, 0);
|
||||
int rc2 = readJournalFile(p, pMain);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
@@ -575,7 +578,7 @@ static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
jt_file *p = (jt_file *)pFile;
|
||||
if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){
|
||||
/* Truncating a journal file. This is the end of a transaction. */
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName);
|
||||
jt_file *pMain = locateDatabaseHandle(p->zName, 0);
|
||||
closeTransaction(pMain);
|
||||
}
|
||||
if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
|
||||
@@ -603,11 +606,10 @@ static int jtSync(sqlite3_file *pFile, int flags){
|
||||
** jt_file.pWritable bitvec of the main database file associated with
|
||||
** this journal file.
|
||||
*/
|
||||
pMain = locateDatabaseHandle(p->zName);
|
||||
assert(pMain);
|
||||
pMain = locateDatabaseHandle(p->zName, 0);
|
||||
|
||||
/* Set the bitvec values */
|
||||
if( pMain->pWritable ){
|
||||
if( pMain && pMain->pWritable ){
|
||||
pMain->nSync++;
|
||||
rc = readJournalFile(p, pMain);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -729,7 +731,7 @@ static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
int nPath = (int)strlen(zPath);
|
||||
if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
|
||||
/* Deleting a journal file. The end of a transaction. */
|
||||
jt_file *pMain = locateDatabaseHandle(zPath);
|
||||
jt_file *pMain = locateDatabaseHandle(zPath, 0);
|
||||
if( pMain ){
|
||||
closeTransaction(pMain);
|
||||
}
|
||||
@@ -824,6 +826,10 @@ static int jtCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
|
||||
return g.pVfs->xCurrentTimeInt64(g.pVfs, pTimeOut);
|
||||
}
|
||||
|
||||
static int jtGetLastError(sqlite3_vfs *pVfs, int n, char *z){
|
||||
return g.pVfs->xGetLastError(g.pVfs, n, z);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
** Start of public API.
|
||||
*/
|
||||
|
||||
+40
-35
@@ -14,7 +14,11 @@
|
||||
** memory allocation subsystem.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -306,7 +310,7 @@ static int textToPointer(const char *z, void **pp){
|
||||
**
|
||||
** Raw test interface for sqlite3_malloc().
|
||||
*/
|
||||
static int test_malloc(
|
||||
static int SQLITE_TCLAPI test_malloc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -331,7 +335,7 @@ static int test_malloc(
|
||||
**
|
||||
** Raw test interface for sqlite3_realloc().
|
||||
*/
|
||||
static int test_realloc(
|
||||
static int SQLITE_TCLAPI test_realloc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -360,7 +364,7 @@ static int test_realloc(
|
||||
**
|
||||
** Raw test interface for sqlite3_free().
|
||||
*/
|
||||
static int test_free(
|
||||
static int SQLITE_TCLAPI test_free(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -391,7 +395,7 @@ int sqlite3TestBinToHex(char*,int);
|
||||
** Set a chunk of memory (obtained from malloc, probably) to a
|
||||
** specified hex pattern.
|
||||
*/
|
||||
static int test_memset(
|
||||
static int SQLITE_TCLAPI test_memset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -437,7 +441,7 @@ static int test_memset(
|
||||
**
|
||||
** Return memory as hexadecimal text.
|
||||
*/
|
||||
static int test_memget(
|
||||
static int SQLITE_TCLAPI test_memget(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -484,7 +488,7 @@ static int test_memget(
|
||||
**
|
||||
** Raw test interface for sqlite3_memory_used().
|
||||
*/
|
||||
static int test_memory_used(
|
||||
static int SQLITE_TCLAPI test_memory_used(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -499,7 +503,7 @@ static int test_memory_used(
|
||||
**
|
||||
** Raw test interface for sqlite3_memory_highwater().
|
||||
*/
|
||||
static int test_memory_highwater(
|
||||
static int SQLITE_TCLAPI test_memory_highwater(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -524,7 +528,7 @@ static int test_memory_highwater(
|
||||
** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined
|
||||
** then this routine is a no-op.
|
||||
*/
|
||||
static int test_memdebug_backtrace(
|
||||
static int SQLITE_TCLAPI test_memdebug_backtrace(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -550,7 +554,7 @@ static int test_memdebug_backtrace(
|
||||
**
|
||||
** Write a summary of unfreed memory to FILENAME.
|
||||
*/
|
||||
static int test_memdebug_dump(
|
||||
static int SQLITE_TCLAPI test_memdebug_dump(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -575,7 +579,7 @@ static int test_memdebug_dump(
|
||||
**
|
||||
** Return the total number of times malloc() has been called.
|
||||
*/
|
||||
static int test_memdebug_malloc_count(
|
||||
static int SQLITE_TCLAPI test_memdebug_malloc_count(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -615,7 +619,7 @@ static int test_memdebug_malloc_count(
|
||||
**
|
||||
** To disable simulated failures, use a COUNTER of -1.
|
||||
*/
|
||||
static int test_memdebug_fail(
|
||||
static int SQLITE_TCLAPI test_memdebug_fail(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -681,7 +685,7 @@ static int test_memdebug_fail(
|
||||
** simulated failure occurs. A negative return value indicates that
|
||||
** no malloc() failure is scheduled.
|
||||
*/
|
||||
static int test_memdebug_pending(
|
||||
static int SQLITE_TCLAPI test_memdebug_pending(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -714,7 +718,7 @@ static int sqlite3_memdebug_title_count = 0;
|
||||
**
|
||||
** Each title overwrite the previous.
|
||||
*/
|
||||
static int test_memdebug_settitle(
|
||||
static int SQLITE_TCLAPI test_memdebug_settitle(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -795,7 +799,7 @@ static void test_memdebug_log_clear(void){
|
||||
Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_KEYINTS);
|
||||
}
|
||||
|
||||
static int test_memdebug_log(
|
||||
static int SQLITE_TCLAPI test_memdebug_log(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -893,7 +897,7 @@ static int test_memdebug_log(
|
||||
**
|
||||
** A negative SIZE causes the buffer pointer to be NULL.
|
||||
*/
|
||||
static int test_config_scratch(
|
||||
static int SQLITE_TCLAPI test_config_scratch(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -933,7 +937,7 @@ static int test_config_scratch(
|
||||
**
|
||||
** A negative SIZE causes the buffer pointer to be NULL.
|
||||
*/
|
||||
static int test_config_pagecache(
|
||||
static int SQLITE_TCLAPI test_config_pagecache(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -976,7 +980,7 @@ static int test_config_pagecache(
|
||||
** is certainty. 0 is never. PRNG_SEED is the pseudo-random number generator
|
||||
** seed.
|
||||
*/
|
||||
static int test_alt_pcache(
|
||||
static int SQLITE_TCLAPI test_alt_pcache(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1017,7 +1021,7 @@ static int test_alt_pcache(
|
||||
**
|
||||
** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
|
||||
*/
|
||||
static int test_config_memstatus(
|
||||
static int SQLITE_TCLAPI test_config_memstatus(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1038,7 +1042,7 @@ static int test_config_memstatus(
|
||||
** Usage: sqlite3_config_lookaside SIZE COUNT
|
||||
**
|
||||
*/
|
||||
static int test_config_lookaside(
|
||||
static int SQLITE_TCLAPI test_config_lookaside(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1072,7 +1076,7 @@ static int test_config_lookaside(
|
||||
** is 10KB in size. A BUFID of 0 indicates that the buffer should be NULL
|
||||
** which will cause sqlite3_db_config() to allocate space on its own.
|
||||
*/
|
||||
static int test_db_config_lookaside(
|
||||
static int SQLITE_TCLAPI test_db_config_lookaside(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1093,7 +1097,7 @@ static int test_db_config_lookaside(
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &sz) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[4], &cnt) ) return TCL_ERROR;
|
||||
if( bufid==0 ){
|
||||
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, 0, sz, cnt);
|
||||
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, (void*)0, sz, cnt);
|
||||
}else if( bufid>=1 && bufid<=2 && sz*cnt<=sizeof(azBuf[0]) ){
|
||||
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, azBuf[bufid], sz,cnt);
|
||||
}else{
|
||||
@@ -1107,7 +1111,7 @@ static int test_db_config_lookaside(
|
||||
/*
|
||||
** Usage: sqlite3_config_heap NBYTE NMINALLOC
|
||||
*/
|
||||
static int test_config_heap(
|
||||
static int SQLITE_TCLAPI test_config_heap(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1144,7 +1148,7 @@ static int test_config_heap(
|
||||
/*
|
||||
** Usage: sqlite3_config_heap_size NBYTE
|
||||
*/
|
||||
static int test_config_heap_size(
|
||||
static int SQLITE_TCLAPI test_config_heap_size(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1174,7 +1178,7 @@ static int test_config_heap_size(
|
||||
** Invoke sqlite3_config() or sqlite3_db_config() with invalid
|
||||
** opcodes and verify that they return errors.
|
||||
*/
|
||||
static int test_config_error(
|
||||
static int SQLITE_TCLAPI test_config_error(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1212,7 +1216,7 @@ static int test_config_error(
|
||||
** Enables or disables interpretation of URI parameters by default using
|
||||
** SQLITE_CONFIG_URI.
|
||||
*/
|
||||
static int test_config_uri(
|
||||
static int SQLITE_TCLAPI test_config_uri(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1241,7 +1245,7 @@ static int test_config_uri(
|
||||
** Enables or disables the use of the covering-index scan optimization.
|
||||
** SQLITE_CONFIG_COVERING_INDEX_SCAN.
|
||||
*/
|
||||
static int test_config_cis(
|
||||
static int SQLITE_TCLAPI test_config_cis(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1269,7 +1273,7 @@ static int test_config_cis(
|
||||
**
|
||||
** Set the minimum PMA size.
|
||||
*/
|
||||
static int test_config_pmasz(
|
||||
static int SQLITE_TCLAPI test_config_pmasz(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1299,7 +1303,7 @@ static int test_config_pmasz(
|
||||
**
|
||||
** Write a summary of unfreed memsys3 allocations to FILENAME.
|
||||
*/
|
||||
static int test_dump_memsys3(
|
||||
static int SQLITE_TCLAPI test_dump_memsys3(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1335,7 +1339,7 @@ static int test_dump_memsys3(
|
||||
** Return a list of three elements which are the sqlite3_status() return
|
||||
** code, the current value, and the high-water mark value.
|
||||
*/
|
||||
static int test_status(
|
||||
static int SQLITE_TCLAPI test_status(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1392,7 +1396,7 @@ static int test_status(
|
||||
** Return a list of three elements which are the sqlite3_db_status() return
|
||||
** code, the current value, and the high-water mark value.
|
||||
*/
|
||||
static int test_db_status(
|
||||
static int SQLITE_TCLAPI test_db_status(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1417,7 +1421,8 @@ static int test_db_status(
|
||||
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
|
||||
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS },
|
||||
{ "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE },
|
||||
{ "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS }
|
||||
{ "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS },
|
||||
{ "CACHE_USED_SHARED", SQLITE_DBSTATUS_CACHE_USED_SHARED },
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
@@ -1452,7 +1457,7 @@ static int test_db_status(
|
||||
/*
|
||||
** install_malloc_faultsim BOOLEAN
|
||||
*/
|
||||
static int test_install_malloc_faultsim(
|
||||
static int SQLITE_TCLAPI test_install_malloc_faultsim(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1476,7 +1481,7 @@ static int test_install_malloc_faultsim(
|
||||
/*
|
||||
** sqlite3_install_memsys3
|
||||
*/
|
||||
static int test_install_memsys3(
|
||||
static int SQLITE_TCLAPI test_install_memsys3(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1491,7 +1496,7 @@ static int test_install_memsys3(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_vfs_oom_test(
|
||||
static int SQLITE_TCLAPI test_vfs_oom_test(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+20
-9
@@ -539,7 +539,7 @@ static int multiplexOpen(
|
||||
memset(pGroup, 0, sz);
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
pGroup->bEnabled = (unsigned char)-1;
|
||||
pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
|
||||
pGroup->bTruncate = (unsigned char)sqlite3_uri_boolean(zUri, "truncate",
|
||||
(flags & SQLITE_OPEN_MAIN_DB)==0);
|
||||
pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize",
|
||||
SQLITE_MULTIPLEX_CHUNK_SIZE);
|
||||
@@ -717,7 +717,11 @@ static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
|
||||
return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
|
||||
}
|
||||
static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
|
||||
return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
|
||||
if( gMultiplex.pOrigVfs->xGetLastError ){
|
||||
return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
|
||||
return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
|
||||
@@ -974,7 +978,7 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
case MULTIPLEX_CTRL_ENABLE:
|
||||
if( pArg ) {
|
||||
int bEnabled = *(int *)pArg;
|
||||
pGroup->bEnabled = bEnabled;
|
||||
pGroup->bEnabled = (unsigned char)bEnabled;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
break;
|
||||
@@ -1193,7 +1197,7 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
|
||||
sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
|
||||
|
||||
sqlite3_auto_extension((void*)multiplexFuncInit);
|
||||
sqlite3_auto_extension((void(*)(void))multiplexFuncInit);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -1225,14 +1229,21 @@ int sqlite3_multiplex_shutdown(int eForce){
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
|
||||
*/
|
||||
static int test_multiplex_initialize(
|
||||
static int SQLITE_TCLAPI test_multiplex_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1263,7 +1274,7 @@ static int test_multiplex_initialize(
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_shutdown
|
||||
*/
|
||||
static int test_multiplex_shutdown(
|
||||
static int SQLITE_TCLAPI test_multiplex_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1291,7 +1302,7 @@ static int test_multiplex_shutdown(
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_dump
|
||||
*/
|
||||
static int test_multiplex_dump(
|
||||
static int SQLITE_TCLAPI test_multiplex_dump(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1346,7 +1357,7 @@ static int test_multiplex_dump(
|
||||
/*
|
||||
** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
|
||||
*/
|
||||
static int test_multiplex_control(
|
||||
static int SQLITE_TCLAPI test_multiplex_control(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef _TEST_MULTIPLEX_H
|
||||
#define _TEST_MULTIPLEX_H
|
||||
#ifndef SQLITE_TEST_MULTIPLEX_H
|
||||
#define SQLITE_TEST_MULTIPLEX_H
|
||||
|
||||
/*
|
||||
** CAPI: File-control Operations Supported by Multiplex VFS
|
||||
@@ -96,4 +96,4 @@ extern int sqlite3_multiplex_shutdown(int eForce);
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* _TEST_MULTIPLEX_H */
|
||||
#endif /* SQLITE_TEST_MULTIPLEX_H */
|
||||
|
||||
+16
-12
@@ -12,7 +12,11 @@
|
||||
** This file contains test logic for the sqlite3_mutex interfaces.
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -152,7 +156,7 @@ static void counterMutexLeave(sqlite3_mutex *p){
|
||||
/*
|
||||
** sqlite3_shutdown
|
||||
*/
|
||||
static int test_shutdown(
|
||||
static int SQLITE_TCLAPI test_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -173,7 +177,7 @@ static int test_shutdown(
|
||||
/*
|
||||
** sqlite3_initialize
|
||||
*/
|
||||
static int test_initialize(
|
||||
static int SQLITE_TCLAPI test_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -194,7 +198,7 @@ static int test_initialize(
|
||||
/*
|
||||
** install_mutex_counters BOOLEAN
|
||||
*/
|
||||
static int test_install_mutex_counters(
|
||||
static int SQLITE_TCLAPI test_install_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -255,7 +259,7 @@ static int test_install_mutex_counters(
|
||||
/*
|
||||
** read_mutex_counters
|
||||
*/
|
||||
static int test_read_mutex_counters(
|
||||
static int SQLITE_TCLAPI test_read_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -284,7 +288,7 @@ static int test_read_mutex_counters(
|
||||
/*
|
||||
** clear_mutex_counters
|
||||
*/
|
||||
static int test_clear_mutex_counters(
|
||||
static int SQLITE_TCLAPI test_clear_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -308,7 +312,7 @@ static int test_clear_mutex_counters(
|
||||
** will be invalid since the mutex has already been freed. The
|
||||
** return pointer just checks to see if the mutex really was allocated.
|
||||
*/
|
||||
static int test_alloc_mutex(
|
||||
static int SQLITE_TCLAPI test_alloc_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -335,7 +339,7 @@ static int test_alloc_mutex(
|
||||
**
|
||||
** Or OPTION can be an raw integer.
|
||||
*/
|
||||
static int test_config(
|
||||
static int SQLITE_TCLAPI test_config(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -397,7 +401,7 @@ static sqlite3_mutex *getStaticMutexPointer(
|
||||
return counterMutexAlloc(iMutex);
|
||||
}
|
||||
|
||||
static int test_enter_static_mutex(
|
||||
static int SQLITE_TCLAPI test_enter_static_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -416,7 +420,7 @@ static int test_enter_static_mutex(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_leave_static_mutex(
|
||||
static int SQLITE_TCLAPI test_leave_static_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -435,7 +439,7 @@ static int test_leave_static_mutex(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_enter_db_mutex(
|
||||
static int SQLITE_TCLAPI test_enter_db_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -454,7 +458,7 @@ static int test_enter_db_mutex(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_leave_db_mutex(
|
||||
static int SQLITE_TCLAPI test_leave_db_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+1
-1
@@ -508,7 +508,7 @@ static int fsSync(sqlite3_file *pFile, int flags){
|
||||
if( p->eType==DATABASE_FILE ){
|
||||
unsigned char zSize[4];
|
||||
zSize[0] = (pReal->nDatabase&0xFF000000)>>24;
|
||||
zSize[1] = (pReal->nDatabase&0x00FF0000)>>16;
|
||||
zSize[1] = (unsigned char)((pReal->nDatabase&0x00FF0000)>>16);
|
||||
zSize[2] = (pReal->nDatabase&0x0000FF00)>>8;
|
||||
zSize[3] = (pReal->nDatabase&0x000000FF);
|
||||
rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
|
||||
|
||||
+12
-5
@@ -644,9 +644,9 @@ static void vfslog_flush(VfslogVfs *p){
|
||||
|
||||
static void put32bits(unsigned char *p, unsigned int v){
|
||||
p[0] = v>>24;
|
||||
p[1] = v>>16;
|
||||
p[2] = v>>8;
|
||||
p[3] = v;
|
||||
p[1] = (unsigned char)(v>>16);
|
||||
p[2] = (unsigned char)(v>>8);
|
||||
p[3] = (unsigned char)v;
|
||||
}
|
||||
|
||||
static void vfslog_call(
|
||||
@@ -1104,9 +1104,16 @@ int sqlite3_vfslog_register(sqlite3 *db){
|
||||
|
||||
#if defined(SQLITE_TEST) || defined(TCLSH)
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static int test_vfslog(
|
||||
static int SQLITE_TCLAPI test_vfslog(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+43
-36
@@ -111,7 +111,7 @@ struct quotaFile {
|
||||
|
||||
/*
|
||||
** An instance of the following object represents each open connection
|
||||
** to a file that participates in quota tracking. This object is a
|
||||
** to a file that participates in quota tracking. This object is a
|
||||
** subclass of sqlite3_file. The sqlite3_file object for the underlying
|
||||
** VFS is appended to this structure.
|
||||
*/
|
||||
@@ -154,11 +154,11 @@ static struct {
|
||||
*/
|
||||
sqlite3_vfs sThisVfs;
|
||||
|
||||
/* The sIoMethods defines the methods used by sqlite3_file objects
|
||||
/* The sIoMethods defines the methods used by sqlite3_file objects
|
||||
** associated with this shim. It is initialized at start-time and does
|
||||
** not require a mutex.
|
||||
**
|
||||
** When the underlying VFS is called to open a file, it might return
|
||||
** When the underlying VFS is called to open a file, it might return
|
||||
** either a version 1 or a version 2 sqlite3_file object. This shim
|
||||
** has to create a wrapper sqlite3_file of the same version. Hence
|
||||
** there are two I/O method structures, one for version 1 and the other
|
||||
@@ -190,7 +190,7 @@ static struct {
|
||||
static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
|
||||
static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
|
||||
|
||||
/* Count the number of open files in a quotaGroup
|
||||
/* Count the number of open files in a quotaGroup
|
||||
*/
|
||||
static int quotaGroupOpenFileCount(quotaGroup *pGroup){
|
||||
int N = 0;
|
||||
@@ -399,7 +399,7 @@ static char *quota_utf8_to_mbcs(const char *zUtf8){
|
||||
return zMbcs;
|
||||
#else
|
||||
return (char*)zUtf8; /* No-op on unix */
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -410,7 +410,7 @@ static void quota_mbcs_free(char *zOld){
|
||||
sqlite3_free(zOld);
|
||||
#else
|
||||
/* No-op on unix */
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
@@ -428,7 +428,7 @@ static int quotaOpen(
|
||||
int flags, /* Flags to control the opening */
|
||||
int *pOutFlags /* Flags showing results of opening */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
int rc; /* Result code */
|
||||
quotaConn *pQuotaOpen; /* The new quota file descriptor */
|
||||
quotaFile *pFile; /* Corresponding quotaFile obj */
|
||||
quotaGroup *pGroup; /* The group file belongs to */
|
||||
@@ -488,7 +488,7 @@ static int quotaDelete(
|
||||
const char *zName, /* Name of file to be deleted */
|
||||
int syncDir /* Do a directory sync after deleting */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
int rc; /* Result code */
|
||||
quotaFile *pFile; /* Files in the quota */
|
||||
quotaGroup *pGroup; /* The group file belongs to */
|
||||
sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
|
||||
@@ -581,7 +581,7 @@ static int quotaWrite(
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
if( pGroup->xCallback ){
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->pArg);
|
||||
}
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
@@ -738,7 +738,7 @@ static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
|
||||
/*
|
||||
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The quota VFS shim is named "quota". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
@@ -908,7 +908,7 @@ int sqlite3_quota_file(const char *zFilename){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
zFull[strlen(zFull)+1] = '\0';
|
||||
rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
|
||||
rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
|
||||
SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xFileSize(fd, &iSize);
|
||||
@@ -1016,7 +1016,7 @@ size_t sqlite3_quota_fwrite(
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
if( pGroup->xCallback ){
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->pArg);
|
||||
}
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
@@ -1203,7 +1203,7 @@ sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){
|
||||
sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
|
||||
return p->pFile ? p->pFile->iSize : -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Determine the amount of data in bytes available for reading
|
||||
** in the given file.
|
||||
@@ -1275,10 +1275,17 @@ int sqlite3_quota_remove(const char *zFilename){
|
||||
sqlite3_free(zFull);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Argument passed to a TCL quota-over-limit callback.
|
||||
@@ -1350,7 +1357,7 @@ static void tclCallbackDestructor(void *pObj){
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
|
||||
*/
|
||||
static int test_quota_initialize(
|
||||
static int SQLITE_TCLAPI test_quota_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1379,7 +1386,7 @@ static int test_quota_initialize(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_shutdown
|
||||
*/
|
||||
static int test_quota_shutdown(
|
||||
static int SQLITE_TCLAPI test_quota_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1402,7 +1409,7 @@ static int test_quota_shutdown(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
|
||||
*/
|
||||
static int test_quota_set(
|
||||
static int SQLITE_TCLAPI test_quota_set(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1456,7 +1463,7 @@ static int test_quota_set(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file FILENAME
|
||||
*/
|
||||
static int test_quota_file(
|
||||
static int SQLITE_TCLAPI test_quota_file(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1482,7 +1489,7 @@ static int test_quota_file(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_dump
|
||||
*/
|
||||
static int test_quota_dump(
|
||||
static int SQLITE_TCLAPI test_quota_dump(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1530,7 +1537,7 @@ static int test_quota_dump(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fopen FILENAME MODE
|
||||
*/
|
||||
static int test_quota_fopen(
|
||||
static int SQLITE_TCLAPI test_quota_fopen(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1560,7 +1567,7 @@ extern void *sqlite3TestTextToPtr(const char*);
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
|
||||
*/
|
||||
static int test_quota_fread(
|
||||
static int SQLITE_TCLAPI test_quota_fread(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1594,7 +1601,7 @@ static int test_quota_fread(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
|
||||
*/
|
||||
static int test_quota_fwrite(
|
||||
static int SQLITE_TCLAPI test_quota_fwrite(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1622,7 +1629,7 @@ static int test_quota_fwrite(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fclose HANDLE
|
||||
*/
|
||||
static int test_quota_fclose(
|
||||
static int SQLITE_TCLAPI test_quota_fclose(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1644,7 +1651,7 @@ static int test_quota_fclose(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
|
||||
*/
|
||||
static int test_quota_fflush(
|
||||
static int SQLITE_TCLAPI test_quota_fflush(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1670,7 +1677,7 @@ static int test_quota_fflush(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
|
||||
*/
|
||||
static int test_quota_fseek(
|
||||
static int SQLITE_TCLAPI test_quota_fseek(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1708,7 +1715,7 @@ static int test_quota_fseek(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_rewind HANDLE
|
||||
*/
|
||||
static int test_quota_rewind(
|
||||
static int SQLITE_TCLAPI test_quota_rewind(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1727,7 +1734,7 @@ static int test_quota_rewind(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_ftell HANDLE
|
||||
*/
|
||||
static int test_quota_ftell(
|
||||
static int SQLITE_TCLAPI test_quota_ftell(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1748,7 +1755,7 @@ static int test_quota_ftell(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE
|
||||
*/
|
||||
static int test_quota_ftruncate(
|
||||
static int SQLITE_TCLAPI test_quota_ftruncate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1773,7 +1780,7 @@ static int test_quota_ftruncate(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file_size HANDLE
|
||||
*/
|
||||
static int test_quota_file_size(
|
||||
static int SQLITE_TCLAPI test_quota_file_size(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1794,7 +1801,7 @@ static int test_quota_file_size(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file_truesize HANDLE
|
||||
*/
|
||||
static int test_quota_file_truesize(
|
||||
static int SQLITE_TCLAPI test_quota_file_truesize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1815,7 +1822,7 @@ static int test_quota_file_truesize(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file_mtime HANDLE
|
||||
*/
|
||||
static int test_quota_file_mtime(
|
||||
static int SQLITE_TCLAPI test_quota_file_mtime(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1838,7 +1845,7 @@ static int test_quota_file_mtime(
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_remove FILENAME
|
||||
*/
|
||||
static int test_quota_remove(
|
||||
static int SQLITE_TCLAPI test_quota_remove(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1862,7 +1869,7 @@ static int test_quota_remove(
|
||||
** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
|
||||
** and return 0 if it does not.
|
||||
*/
|
||||
static int test_quota_glob(
|
||||
static int SQLITE_TCLAPI test_quota_glob(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1888,7 +1895,7 @@ static int test_quota_glob(
|
||||
** Return the number of bytes from the current file point to the end of
|
||||
** the file.
|
||||
*/
|
||||
static int test_quota_file_available(
|
||||
static int SQLITE_TCLAPI test_quota_file_available(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1911,7 +1918,7 @@ static int test_quota_file_available(
|
||||
**
|
||||
** Return true if the file handle is in the error state.
|
||||
*/
|
||||
static int test_quota_ferror(
|
||||
static int SQLITE_TCLAPI test_quota_ferror(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+12
-4
@@ -14,7 +14,11 @@
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
@@ -353,7 +357,11 @@ static int bfs_query_func(sqlite3_rtree_query_info *p){
|
||||
*************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
typedef struct Cube Cube;
|
||||
struct Cube {
|
||||
@@ -432,7 +440,7 @@ static int cube_geom(
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
|
||||
static int register_cube_geom(
|
||||
static int SQLITE_TCLAPI register_cube_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -460,7 +468,7 @@ static int register_cube_geom(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int register_circle_geom(
|
||||
static int SQLITE_TCLAPI register_circle_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+8
-4
@@ -35,10 +35,14 @@
|
||||
** to be compiled into an sqlite dynamic extension.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
# include "sqliteInt.h"
|
||||
# if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
# else
|
||||
# include "tcl.h"
|
||||
# endif
|
||||
#else
|
||||
#include "sqlite3ext.h"
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
@@ -302,7 +306,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
/*
|
||||
** Register the schema virtual table module.
|
||||
*/
|
||||
static int register_schema_module(
|
||||
static int SQLITE_TCLAPI register_schema_module(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+11
-4
@@ -256,7 +256,14 @@ int sqlite3demo_superlock(
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
struct InterpAndScript {
|
||||
Tcl_Interp *interp;
|
||||
@@ -264,11 +271,11 @@ struct InterpAndScript {
|
||||
};
|
||||
typedef struct InterpAndScript InterpAndScript;
|
||||
|
||||
static void superunlock_del(ClientData cd){
|
||||
static void SQLITE_TCLAPI superunlock_del(ClientData cd){
|
||||
sqlite3demo_superunlock((void *)cd);
|
||||
}
|
||||
|
||||
static int superunlock_cmd(
|
||||
static int SQLITE_TCLAPI superunlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -300,7 +307,7 @@ static int superlock_busy(void *pCtx, int nBusy){
|
||||
/*
|
||||
** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
|
||||
*/
|
||||
static int superlock_cmd(
|
||||
static int SQLITE_TCLAPI superlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+33
-20
@@ -76,7 +76,11 @@
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "sqlite3.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -108,10 +112,13 @@ static int ts_ftruncate(int fd, off_t n);
|
||||
static int ts_fcntl(int fd, int cmd, ... );
|
||||
static int ts_read(int fd, void *aBuf, size_t nBuf);
|
||||
static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
|
||||
static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off);
|
||||
/* Note: pread64() and pwrite64() actually use off64_t as the type on their
|
||||
** last parameter. But that datatype is not defined on many systems
|
||||
** (ex: Mac, OpenBSD). So substitute a likely equivalent: sqlite3_uint64 */
|
||||
static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
static int ts_write(int fd, const void *aBuf, size_t nBuf);
|
||||
static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
|
||||
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off);
|
||||
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
static int ts_fchmod(int fd, mode_t mode);
|
||||
static int ts_fallocate(int fd, off_t off, off_t len);
|
||||
static void *ts_mmap(void *, size_t, int, int, int, off_t);
|
||||
@@ -155,11 +162,11 @@ struct TestSyscallArray {
|
||||
#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig)
|
||||
#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig)
|
||||
#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig)
|
||||
#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig)
|
||||
#define orig_pread64 ((ssize_t(*)(int,void*,size_t,sqlite3_uint64))aSyscall[10].xOrig)
|
||||
#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig)
|
||||
#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
|
||||
aSyscall[12].xOrig)
|
||||
#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
|
||||
#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,sqlite3_uint64))\
|
||||
aSyscall[13].xOrig)
|
||||
#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig)
|
||||
#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
|
||||
@@ -326,7 +333,7 @@ static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
|
||||
/*
|
||||
** A wrapper around pread64().
|
||||
*/
|
||||
static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){
|
||||
static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
if( tsIsFailErrno("pread64") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -357,7 +364,7 @@ static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
|
||||
/*
|
||||
** A wrapper around pwrite64().
|
||||
*/
|
||||
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){
|
||||
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
if( tsIsFailErrno("pwrite64") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -415,7 +422,7 @@ static void *ts_mremap(void *a, size_t b, size_t c, int d, ...){
|
||||
return orig_mremap(a, b, c, d, pArg);
|
||||
}
|
||||
|
||||
static int test_syscall_install(
|
||||
static int SQLITE_TCLAPI test_syscall_install(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -451,7 +458,7 @@ static int test_syscall_install(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_uninstall(
|
||||
static int SQLITE_TCLAPI test_syscall_uninstall(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -475,7 +482,7 @@ static int test_syscall_uninstall(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_reset(
|
||||
static int SQLITE_TCLAPI test_syscall_reset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -513,7 +520,7 @@ static int test_syscall_reset(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_exists(
|
||||
static int SQLITE_TCLAPI test_syscall_exists(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -534,7 +541,7 @@ static int test_syscall_exists(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_fault(
|
||||
static int SQLITE_TCLAPI test_syscall_fault(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -563,7 +570,7 @@ static int test_syscall_fault(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_errno(
|
||||
static int SQLITE_TCLAPI test_syscall_errno(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -609,7 +616,7 @@ static int test_syscall_errno(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_list(
|
||||
static int SQLITE_TCLAPI test_syscall_list(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -639,7 +646,7 @@ static int test_syscall_list(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall_defaultvfs(
|
||||
static int SQLITE_TCLAPI test_syscall_defaultvfs(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -661,7 +668,7 @@ static int ts_getpagesize(void){
|
||||
return gSyscall.pgsz;
|
||||
}
|
||||
|
||||
static int test_syscall_pagesize(
|
||||
static int SQLITE_TCLAPI test_syscall_pagesize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -696,7 +703,7 @@ static int test_syscall_pagesize(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_syscall(
|
||||
static int SQLITE_TCLAPI test_syscall(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -719,14 +726,20 @@ static int test_syscall(
|
||||
};
|
||||
int iCmd;
|
||||
int rc;
|
||||
sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = Tcl_GetIndexFromObjStruct(interp,
|
||||
objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
|
||||
);
|
||||
if( pVfs->iVersion<3 || pVfs->xSetSystemCall==0 ){
|
||||
Tcl_AppendResult(interp, "VFS does not support xSetSystemCall", 0);
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
rc = Tcl_GetIndexFromObjStruct(interp,
|
||||
objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
|
||||
);
|
||||
}
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
return aCmd[iCmd].xCmd(clientData, interp, objc, objv);
|
||||
}
|
||||
|
||||
+6
-2
@@ -17,7 +17,11 @@
|
||||
** access to TCL variables.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -407,7 +411,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_tclvar_module(
|
||||
static int SQLITE_TCLAPI register_tclvar_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
|
||||
+16
-12
@@ -16,7 +16,11 @@
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#if SQLITE_THREADSAFE
|
||||
|
||||
@@ -72,7 +76,7 @@ extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
|
||||
/*
|
||||
** Handler for events of type EvalEvent.
|
||||
*/
|
||||
static int tclScriptEvent(Tcl_Event *evPtr, int flags){
|
||||
static int SQLITE_TCLAPI tclScriptEvent(Tcl_Event *evPtr, int flags){
|
||||
int rc;
|
||||
EvalEvent *p = (EvalEvent *)evPtr;
|
||||
rc = Tcl_Eval(p->interp, p->zScript);
|
||||
@@ -167,7 +171,7 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
|
||||
**
|
||||
** The caller can wait for the script to terminate using [vwait VARNAME].
|
||||
*/
|
||||
static int sqlthread_spawn(
|
||||
static int SQLITE_TCLAPI sqlthread_spawn(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -220,7 +224,7 @@ static int sqlthread_spawn(
|
||||
**
|
||||
** NOTE: At the moment, this doesn't work. FIXME.
|
||||
*/
|
||||
static int sqlthread_parent(
|
||||
static int SQLITE_TCLAPI sqlthread_parent(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -265,7 +269,7 @@ static int xBusy(void *pArg, int nBusy){
|
||||
** Open a database handle and return the string representation of
|
||||
** the pointer value.
|
||||
*/
|
||||
static int sqlthread_open(
|
||||
static int SQLITE_TCLAPI sqlthread_open(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -276,7 +280,7 @@ static int sqlthread_open(
|
||||
const char *zFilename;
|
||||
sqlite3 *db;
|
||||
char zBuf[100];
|
||||
extern void Md5_Register(sqlite3*);
|
||||
extern int Md5_Register(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(objc);
|
||||
@@ -299,7 +303,7 @@ static int sqlthread_open(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Md5_Register(db);
|
||||
Md5_Register(db, 0, 0);
|
||||
sqlite3_busy_handler(db, xBusy, 0);
|
||||
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
|
||||
@@ -315,7 +319,7 @@ static int sqlthread_open(
|
||||
** Return the current thread-id (Tcl_GetCurrentThread()) cast to
|
||||
** an integer.
|
||||
*/
|
||||
static int sqlthread_id(
|
||||
static int SQLITE_TCLAPI sqlthread_id(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -333,7 +337,7 @@ static int sqlthread_id(
|
||||
/*
|
||||
** Dispatch routine for the sub-commands of [sqlthread].
|
||||
*/
|
||||
static int sqlthread_proc(
|
||||
static int SQLITE_TCLAPI sqlthread_proc(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -381,7 +385,7 @@ static int sqlthread_proc(
|
||||
** implemented as a script in Tcl 8.5, it is not usually available to
|
||||
** testfixture.
|
||||
*/
|
||||
static int clock_seconds_proc(
|
||||
static int SQLITE_TCLAPI clock_seconds_proc(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -543,7 +547,7 @@ int sqlite3_blocking_prepare_v2(
|
||||
**
|
||||
** Advance the statement to the next row.
|
||||
*/
|
||||
static int blocking_step_proc(
|
||||
static int SQLITE_TCLAPI blocking_step_proc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -569,7 +573,7 @@ static int blocking_step_proc(
|
||||
** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
|
||||
** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
|
||||
*/
|
||||
static int blocking_prepare_v2_proc(
|
||||
static int SQLITE_TCLAPI blocking_prepare_v2_proc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+10
-7
@@ -28,7 +28,11 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
typedef struct Testvfs Testvfs;
|
||||
typedef struct TestvfsShm TestvfsShm;
|
||||
@@ -306,7 +310,6 @@ static void tvfsExecTcl(
|
||||
** Close an tvfs-file.
|
||||
*/
|
||||
static int tvfsClose(sqlite3_file *pFile){
|
||||
int rc;
|
||||
TestvfsFile *pTestfile = (TestvfsFile *)pFile;
|
||||
TestvfsFd *pFd = pTestfile->pFd;
|
||||
Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
|
||||
@@ -324,10 +327,10 @@ static int tvfsClose(sqlite3_file *pFile){
|
||||
if( pFile->pMethods ){
|
||||
ckfree((char *)pFile->pMethods);
|
||||
}
|
||||
rc = sqlite3OsClose(pFd->pReal);
|
||||
sqlite3OsClose(pFd->pReal);
|
||||
ckfree((char *)pFd);
|
||||
pTestfile->pFd = 0;
|
||||
return rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1037,7 +1040,7 @@ static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){
|
||||
return sqlite3OsUnfetch(pFd->pReal, iOfst, p);
|
||||
}
|
||||
|
||||
static int testvfs_obj_cmd(
|
||||
static int SQLITE_TCLAPI testvfs_obj_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1349,7 +1352,7 @@ static int testvfs_obj_cmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void testvfs_obj_del(ClientData cd){
|
||||
static void SQLITE_TCLAPI testvfs_obj_del(ClientData cd){
|
||||
Testvfs *p = (Testvfs *)cd;
|
||||
if( p->pScript ) Tcl_DecrRefCount(p->pScript);
|
||||
sqlite3_vfs_unregister(p->pVfs);
|
||||
@@ -1392,7 +1395,7 @@ static void testvfs_obj_del(ClientData cd){
|
||||
**
|
||||
** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
|
||||
*/
|
||||
static int testvfs_cmd(
|
||||
static int SQLITE_TCLAPI testvfs_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+29
-1
@@ -17,6 +17,34 @@
|
||||
|
||||
#include "test_windirent.h"
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX getenv() function using the Win32 API.
|
||||
** This function is not thread-safe.
|
||||
*/
|
||||
const char *windirent_getenv(
|
||||
const char *name
|
||||
){
|
||||
static char value[32768]; /* Maximum length, per MSDN */
|
||||
DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
|
||||
DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
dwRet = GetEnvironmentVariableA(name, value, dwSize);
|
||||
if( dwRet==0 || dwRet>dwSize ){
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() failed -OR-
|
||||
** the buffer is not large enough. Either way, return NULL.
|
||||
*/
|
||||
return 0;
|
||||
}else{
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() succeeded
|
||||
** -AND- the buffer contains the entire value.
|
||||
*/
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX opendir() function using the MSVCRT.
|
||||
*/
|
||||
@@ -32,7 +60,7 @@ LPDIR opendir(
|
||||
|
||||
/* TODO: Remove this if Unix-style root paths are not used. */
|
||||
if( sqlite3_stricmp(dirname, "/")==0 ){
|
||||
dirname = getenv("SystemDrive");
|
||||
dirname = windirent_getenv("SystemDrive");
|
||||
}
|
||||
|
||||
_snprintf(data.name, namesize, "%s\\*", dirname);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user