mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
Merge branch 'sqlite-release' into v2-integration
Conflicts: Makefile.in
This commit is contained in:
@@ -313,6 +313,11 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
|
||||
}
|
||||
}
|
||||
}
|
||||
if( zWhere ){
|
||||
char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere);
|
||||
sqlite3DbFree(pParse->db, zWhere);
|
||||
zWhere = zNew;
|
||||
}
|
||||
return zWhere;
|
||||
}
|
||||
|
||||
|
||||
+83
-27
@@ -113,7 +113,8 @@ static void analyzeOneTable(
|
||||
int i; /* Loop counter */
|
||||
int topOfLoop; /* The top of the loop */
|
||||
int endOfLoop; /* The end of the loop */
|
||||
int addr; /* The address of an instruction */
|
||||
int addr = 0; /* The address of an instruction */
|
||||
int jZeroRows = 0; /* Jump from here if number of rows is zero */
|
||||
int iDb; /* Index of database containing pTab */
|
||||
int regTabname = iMem++; /* Register containing table name */
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
@@ -132,8 +133,15 @@ static void analyzeOneTable(
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){
|
||||
/* Do no analysis for tables that have no indices */
|
||||
if( v==0 || NEVER(pTab==0) ){
|
||||
return;
|
||||
}
|
||||
if( pTab->tnum==0 ){
|
||||
/* Do not gather statistics on views or virtual tables */
|
||||
return;
|
||||
}
|
||||
if( memcmp(pTab->zName, "sqlite_", 7)==0 ){
|
||||
/* Do not gather statistics on system tables */
|
||||
return;
|
||||
}
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
@@ -150,6 +158,7 @@ static void analyzeOneTable(
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
iIdxCur = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol = pIdx->nColumn;
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
@@ -164,10 +173,7 @@ static void analyzeOneTable(
|
||||
(char *)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
||||
/* Populate the registers containing the table and index names. */
|
||||
if( pTab->pIndex==pIdx ){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
|
||||
}
|
||||
/* Populate the register containing the index name. */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
@@ -227,9 +233,10 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
|
||||
|
||||
for(i=0; i<nCol; i++){
|
||||
CollSeq *pColl;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
if( i==0 ){
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
/* Check if the record that cursor iIdxCur points to contains a
|
||||
** value that should be stored in the sqlite_stat2 table. If so,
|
||||
** store it. */
|
||||
@@ -258,12 +265,17 @@ static void analyzeOneTable(
|
||||
|
||||
sqlite3VdbeJumpHere(v, ne);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1);
|
||||
/**** TODO: add collating sequence *****/
|
||||
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
||||
/* Always record the very first row */
|
||||
sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
|
||||
}
|
||||
assert( pIdx->azColl!=0 );
|
||||
assert( pIdx->azColl[i]!=0 );
|
||||
pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
|
||||
(char*)pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
/* If a malloc failure has occurred, then the result of the expression
|
||||
@@ -274,7 +286,11 @@ static void analyzeOneTable(
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-(nCol*2));
|
||||
int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
|
||||
if( i==0 ){
|
||||
sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
|
||||
}
|
||||
@@ -302,8 +318,10 @@ static void analyzeOneTable(
|
||||
** If K>0 then it is always the case the D>0 so division by zero
|
||||
** is never possible.
|
||||
*/
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
|
||||
if( jZeroRows==0 ){
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
|
||||
@@ -317,13 +335,35 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
}
|
||||
|
||||
/* If the table has no indices, create a single sqlite_stat1 entry
|
||||
** containing NULL as the index name and the row count as the content.
|
||||
*/
|
||||
if( pTab->pIndex==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
}else{
|
||||
assert( jZeroRows>0 );
|
||||
addr = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
if( pParse->nMem<regRec ) pParse->nMem = regRec;
|
||||
if( jZeroRows ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will cause the most recent index analysis to
|
||||
** be laoded into internal hash tables where is can be used.
|
||||
** be loaded into internal hash tables where is can be used.
|
||||
*/
|
||||
static void loadAnalysis(Parse *pParse, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
@@ -453,33 +493,46 @@ struct analysisInfo {
|
||||
** This callback is invoked once for each index when reading the
|
||||
** sqlite_stat1 table.
|
||||
**
|
||||
** argv[0] = name of the index
|
||||
** argv[1] = results of analysis - on integer for each column
|
||||
** argv[0] = name of the table
|
||||
** argv[1] = name of the index (might be NULL)
|
||||
** argv[2] = results of analysis - on integer for each column
|
||||
**
|
||||
** Entries for which argv[1]==NULL simply record the number of rows in
|
||||
** the table.
|
||||
*/
|
||||
static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
analysisInfo *pInfo = (analysisInfo*)pData;
|
||||
Index *pIndex;
|
||||
int i, c;
|
||||
Table *pTable;
|
||||
int i, c, n;
|
||||
unsigned int v;
|
||||
const char *z;
|
||||
|
||||
assert( argc==2 );
|
||||
assert( argc==3 );
|
||||
UNUSED_PARAMETER2(NotUsed, argc);
|
||||
|
||||
if( argv==0 || argv[0]==0 || argv[1]==0 ){
|
||||
if( argv==0 || argv[0]==0 || argv[2]==0 ){
|
||||
return 0;
|
||||
}
|
||||
pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase);
|
||||
if( pIndex==0 ){
|
||||
pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase);
|
||||
if( pTable==0 ){
|
||||
return 0;
|
||||
}
|
||||
z = argv[1];
|
||||
for(i=0; *z && i<=pIndex->nColumn; i++){
|
||||
if( argv[1] ){
|
||||
pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
|
||||
}else{
|
||||
pIndex = 0;
|
||||
}
|
||||
n = pIndex ? pIndex->nColumn : 0;
|
||||
z = argv[2];
|
||||
for(i=0; *z && i<=n; i++){
|
||||
v = 0;
|
||||
while( (c=z[0])>='0' && c<='9' ){
|
||||
v = v*10 + c - '0';
|
||||
z++;
|
||||
}
|
||||
if( i==0 ) pTable->nRowEst = v;
|
||||
if( pIndex==0 ) break;
|
||||
pIndex->aiRowEst[i] = v;
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
@@ -555,7 +608,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -583,8 +636,11 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
|
||||
if( pIdx ){
|
||||
int iSample = sqlite3_column_int(pStmt, 1);
|
||||
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
|
||||
|
||||
+6
-5
@@ -124,9 +124,8 @@ static void attachFunc(
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialised.
|
||||
*/
|
||||
rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE,
|
||||
db->openFlags | SQLITE_OPEN_MAIN_DB,
|
||||
&aNew->pBt);
|
||||
rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0,
|
||||
db->openFlags | SQLITE_OPEN_MAIN_DB);
|
||||
db->nDb++;
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
rc = SQLITE_ERROR;
|
||||
@@ -367,7 +366,8 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"sqlite_detach", /* zName */
|
||||
0 /* pHash */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
|
||||
}
|
||||
@@ -388,7 +388,8 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"sqlite_attach", /* zName */
|
||||
0 /* pHash */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
|
||||
}
|
||||
|
||||
+54
-23
@@ -117,6 +117,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
return pDb->aDb[i].pBt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to set the page size of the destination to match the page size
|
||||
** of the source.
|
||||
*/
|
||||
static int setDestPgsz(sqlite3_backup *p){
|
||||
int rc;
|
||||
rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create an sqlite3_backup process to copy the contents of zSrcDb from
|
||||
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
|
||||
@@ -150,7 +160,10 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
);
|
||||
p = 0;
|
||||
}else {
|
||||
/* Allocate space for a new sqlite3_backup object */
|
||||
/* Allocate space for a new sqlite3_backup object...
|
||||
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
|
||||
** call to sqlite3_backup_init() and is destroyed by a call to
|
||||
** sqlite3_backup_finish(). */
|
||||
p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
|
||||
if( !p ){
|
||||
sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
|
||||
@@ -167,10 +180,11 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
p->iNext = 1;
|
||||
p->isAttached = 0;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest ){
|
||||
/* One (or both) of the named databases did not exist. An error has
|
||||
** already been written into the pDestDb handle. All that is left
|
||||
** to do here is free the sqlite3_backup structure.
|
||||
if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
** error was hit. The error has already been written into the
|
||||
** pDestDb handle. All that is left to do here is free the
|
||||
** sqlite3_backup structure.
|
||||
*/
|
||||
sqlite3_free(p);
|
||||
p = 0;
|
||||
@@ -427,32 +441,46 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
*/
|
||||
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
|
||||
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
|
||||
i64 iOff;
|
||||
i64 iEnd;
|
||||
|
||||
assert( pFile );
|
||||
assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
|
||||
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
|
||||
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
|
||||
));
|
||||
if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1))
|
||||
&& SQLITE_OK==(rc = backupTruncateFile(pFile, iSize))
|
||||
&& SQLITE_OK==(rc = sqlite3PagerSync(pDestPager))
|
||||
|
||||
/* This call ensures that all data required to recreate the original
|
||||
** database has been stored in the journal for pDestPager and the
|
||||
** journal synced to disk. So at this point we may safely modify
|
||||
** the database file in any way, knowing that if a power failure
|
||||
** occurs, the original database will be reconstructed from the
|
||||
** journal file. */
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
|
||||
|
||||
/* Write the extra pages and truncate the database file as required. */
|
||||
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
|
||||
for(
|
||||
iOff=PENDING_BYTE+pgszSrc;
|
||||
rc==SQLITE_OK && iOff<iEnd;
|
||||
iOff+=pgszSrc
|
||||
){
|
||||
i64 iOff;
|
||||
i64 iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
|
||||
for(
|
||||
iOff=PENDING_BYTE+pgszSrc;
|
||||
rc==SQLITE_OK && iOff<iEnd;
|
||||
iOff+=pgszSrc
|
||||
){
|
||||
PgHdr *pSrcPg = 0;
|
||||
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zData = sqlite3PagerGetData(pSrcPg);
|
||||
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
|
||||
}
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
PgHdr *pSrcPg = 0;
|
||||
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zData = sqlite3PagerGetData(pSrcPg);
|
||||
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
|
||||
}
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = backupTruncateFile(pFile, iSize);
|
||||
}
|
||||
|
||||
/* Sync the database file to disk. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSync(pDestPager);
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
|
||||
@@ -533,6 +561,9 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
}
|
||||
sqlite3BtreeLeave(p->pSrc);
|
||||
if( p->pDestDb ){
|
||||
/* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
|
||||
** call to sqlite3_backup_init() and is destroyed by a call to
|
||||
** sqlite3_backup_finish(). */
|
||||
sqlite3_free(p);
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
|
||||
+68
-26
@@ -918,14 +918,9 @@ static void btreeParseCellPtr(
|
||||
/* This is the (easy) common case where the entire payload fits
|
||||
** on the local page. No overflow is required.
|
||||
*/
|
||||
int nSize; /* Total size of cell content in bytes */
|
||||
nSize = nPayload + n;
|
||||
if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
|
||||
pInfo->nLocal = (u16)nPayload;
|
||||
pInfo->iOverflow = 0;
|
||||
if( (nSize & ~3)==0 ){
|
||||
nSize = 4; /* Minimum cell size is 4 */
|
||||
}
|
||||
pInfo->nSize = (u16)nSize;
|
||||
}else{
|
||||
/* If the payload will not fit completely on the local page, we have
|
||||
** to decide how much to store locally and how much to spill onto
|
||||
@@ -1672,11 +1667,20 @@ static int btreeInvokeBusyHandler(void *pArg){
|
||||
** Open a database file.
|
||||
**
|
||||
** zFilename is the name of the database file. If zFilename is NULL
|
||||
** a new database with a random name is created. This randomly named
|
||||
** database file will be deleted when sqlite3BtreeClose() is called.
|
||||
** then an ephemeral database is created. The ephemeral database might
|
||||
** be exclusively in memory, or it might use a disk-based memory cache.
|
||||
** Either way, the ephemeral database will be automatically deleted
|
||||
** when sqlite3BtreeClose() is called.
|
||||
**
|
||||
** If zFilename is ":memory:" then an in-memory database is created
|
||||
** that is automatically destroyed when it is closed.
|
||||
**
|
||||
** The "flags" parameter is a bitmask that might contain bits
|
||||
** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK
|
||||
** bit is also set if the SQLITE_NoReadlock flags is set in db->flags.
|
||||
** These flags are passed through into sqlite3PagerOpen() and must
|
||||
** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK.
|
||||
**
|
||||
** If the database is already opened in the same database connection
|
||||
** and we are in shared cache mode, then the open will fail with an
|
||||
** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared
|
||||
@@ -1698,22 +1702,38 @@ int sqlite3BtreeOpen(
|
||||
u8 nReserve; /* Byte of unused space on each page */
|
||||
unsigned char zDbHeader[100]; /* Database header content */
|
||||
|
||||
/* True if opening an ephemeral, temporary database */
|
||||
const int isTempDb = zFilename==0 || zFilename[0]==0;
|
||||
|
||||
/* Set the variable isMemdb to true for an in-memory database, or
|
||||
** false for a file-based database. This symbol is only required if
|
||||
** either of the shared-data or autovacuum features are compiled
|
||||
** into the library.
|
||||
** false for a file-based database.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
#ifdef SQLITE_OMIT_MEMORYDB
|
||||
const int isMemdb = 0;
|
||||
#else
|
||||
const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_MEMORYDB
|
||||
const int isMemdb = 0;
|
||||
#else
|
||||
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
|
||||
|| (isTempDb && sqlite3TempInMemory(db));
|
||||
#endif
|
||||
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( (flags&0xff)==flags ); /* flags fit in 8 bits */
|
||||
|
||||
/* Only a BTREE_SINGLE database can be BTREE_UNORDERED */
|
||||
assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 );
|
||||
|
||||
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
|
||||
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
|
||||
|
||||
if( db->flags & SQLITE_NoReadlock ){
|
||||
flags |= BTREE_NO_READLOCK;
|
||||
}
|
||||
if( isMemdb ){
|
||||
flags |= BTREE_MEMORY;
|
||||
}
|
||||
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
|
||||
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
|
||||
}
|
||||
pVfs = db->pVfs;
|
||||
p = sqlite3MallocZero(sizeof(Btree));
|
||||
if( !p ){
|
||||
@@ -1731,7 +1751,7 @@ int sqlite3BtreeOpen(
|
||||
** If this Btree is a candidate for shared cache, try to find an
|
||||
** existing BtShared object that we can share with
|
||||
*/
|
||||
if( isMemdb==0 && zFilename && zFilename[0] ){
|
||||
if( isMemdb==0 && isTempDb==0 ){
|
||||
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
|
||||
int nFullPathname = pVfs->mxPathname+1;
|
||||
char *zFullPathname = sqlite3Malloc(nFullPathname);
|
||||
@@ -1806,6 +1826,7 @@ int sqlite3BtreeOpen(
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto btree_open_out;
|
||||
}
|
||||
pBt->openFlags = (u8)flags;
|
||||
pBt->db = db;
|
||||
sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
|
||||
p->pBt = pBt;
|
||||
@@ -1910,6 +1931,14 @@ btree_open_out:
|
||||
sqlite3_free(pBt);
|
||||
sqlite3_free(p);
|
||||
*ppBtree = 0;
|
||||
}else{
|
||||
/* If the B-Tree was successfully opened, set the pager-cache size to the
|
||||
** default value. Except, when opening on an existing shared pager-cache,
|
||||
** do not change the pager-cache size.
|
||||
*/
|
||||
if( sqlite3BtreeSchema(p, 0, 0)==0 ){
|
||||
sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
}
|
||||
}
|
||||
if( mutexOpen ){
|
||||
assert( sqlite3_mutex_held(mutexOpen) );
|
||||
@@ -2067,11 +2096,17 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
|
||||
** probability of damage to near zero but with a write performance reduction.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
|
||||
int sqlite3BtreeSetSafetyLevel(
|
||||
Btree *p, /* The btree to set the safety level on */
|
||||
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
|
||||
int fullSync, /* PRAGMA fullfsync. */
|
||||
int ckptFullSync /* PRAGMA checkpoint_fullfync */
|
||||
){
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
assert( level>=1 && level<=3 );
|
||||
sqlite3BtreeEnter(p);
|
||||
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
|
||||
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
|
||||
sqlite3BtreeLeave(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -2346,7 +2381,7 @@ static int lockBtree(BtShared *pBt){
|
||||
pageSize-usableSize);
|
||||
return rc;
|
||||
}
|
||||
if( nPageHeader>nPageFile ){
|
||||
if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPageHeader>nPageFile ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto page1_init_failed;
|
||||
}
|
||||
@@ -3129,8 +3164,8 @@ static void btreeEndTransaction(Btree *p){
|
||||
** are no active cursors, it also releases the read lock.
|
||||
*/
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
|
||||
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
|
||||
sqlite3BtreeEnter(p);
|
||||
btreeIntegrity(p);
|
||||
|
||||
@@ -3139,6 +3174,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
|
||||
*/
|
||||
if( p->inTrans==TRANS_WRITE ){
|
||||
int rc;
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
assert( pBt->nTransaction>0 );
|
||||
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
|
||||
@@ -6860,11 +6896,12 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
|
||||
** BTREE_ZERODATA Used for SQL indices
|
||||
*/
|
||||
static int btreeCreateTable(Btree *p, int *piTable, int flags){
|
||||
static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
BtShared *pBt = p->pBt;
|
||||
MemPage *pRoot;
|
||||
Pgno pgnoRoot;
|
||||
int rc;
|
||||
int ptfFlags; /* Page-type flage for the root page of new table */
|
||||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
@@ -6983,8 +7020,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
|
||||
}
|
||||
#endif
|
||||
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
|
||||
zeroPage(pRoot, flags | PTF_LEAF);
|
||||
if( createTabFlags & BTREE_INTKEY ){
|
||||
ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF;
|
||||
}else{
|
||||
ptfFlags = PTF_ZERODATA | PTF_LEAF;
|
||||
}
|
||||
zeroPage(pRoot, ptfFlags);
|
||||
sqlite3PagerUnref(pRoot->pDbPage);
|
||||
assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
|
||||
*piTable = (int)pgnoRoot;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -8050,8 +8093,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
||||
void sqlite3BtreeCacheOverflow(BtCursor *pCur){
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
assert(!pCur->isIncrblobHandle);
|
||||
assert(!pCur->aOverflow);
|
||||
invalidateOverflowCache(pCur);
|
||||
pCur->isIncrblobHandle = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
+14
-9
@@ -67,16 +67,15 @@ int sqlite3BtreeOpen(
|
||||
** NOTE: These values must match the corresponding PAGER_ values in
|
||||
** pager.h.
|
||||
*/
|
||||
#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */
|
||||
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
|
||||
#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
|
||||
#define BTREE_MEMORY 4 /* In-memory DB. No argument */
|
||||
#define BTREE_READONLY 8 /* Open the database in read-only mode */
|
||||
#define BTREE_READWRITE 16 /* Open for both reading and writing */
|
||||
#define BTREE_CREATE 32 /* Create the database if it does not exist */
|
||||
#define BTREE_MEMORY 4 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
|
||||
int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
|
||||
int sqlite3BtreeSyncDisabled(Btree*);
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
|
||||
int sqlite3BtreeGetPageSize(Btree*);
|
||||
@@ -108,11 +107,17 @@ int sqlite3BtreeCopyFile(Btree *, Btree *);
|
||||
int sqlite3BtreeIncrVacuum(Btree *);
|
||||
|
||||
/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
|
||||
** of the following flags:
|
||||
** of the flags shown below.
|
||||
**
|
||||
** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set.
|
||||
** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data
|
||||
** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With
|
||||
** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored
|
||||
** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL
|
||||
** indices.)
|
||||
*/
|
||||
#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */
|
||||
#define BTREE_ZERODATA 2 /* Table has keys only - no data */
|
||||
#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */
|
||||
#define BTREE_BLOBKEY 2 /* Table has keys only - no data */
|
||||
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int, int*);
|
||||
|
||||
+9
-8
@@ -409,16 +409,17 @@ struct BtShared {
|
||||
u8 pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
u8 secureDelete; /* True if secure_delete is enabled */
|
||||
u8 initiallyEmpty; /* Database is empty at start of transaction */
|
||||
u8 openFlags; /* Flags to sqlite3BtreeOpen() */
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
u8 autoVacuum; /* True if auto-vacuum is enabled */
|
||||
u8 incrVacuum; /* True if incr-vacuum is enabled */
|
||||
#endif
|
||||
u8 inTransaction; /* Transaction state */
|
||||
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
|
||||
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
|
||||
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
|
||||
u8 inTransaction; /* Transaction state */
|
||||
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
|
||||
u32 pageSize; /* Total number of bytes on a page */
|
||||
u32 usableSize; /* Number of usable bytes on each page */
|
||||
int nTransaction; /* Number of open transactions (read + write) */
|
||||
@@ -445,8 +446,8 @@ struct BtShared {
|
||||
*/
|
||||
typedef struct CellInfo CellInfo;
|
||||
struct CellInfo {
|
||||
u8 *pCell; /* Pointer to the start of cell content */
|
||||
i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
|
||||
u8 *pCell; /* Pointer to the start of cell content */
|
||||
u32 nData; /* Number of bytes of data */
|
||||
u32 nPayload; /* Total amount of payload */
|
||||
u16 nHeader; /* Size of the cell content header in bytes */
|
||||
@@ -488,20 +489,20 @@ struct BtCursor {
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
void *pKey; /* Saved key that was cursor's last known position */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
|
||||
u8 wrFlag; /* True if writable */
|
||||
u8 atLast; /* Cursor pointing to the last entry */
|
||||
u8 validNKey; /* True if info.nKey is valid */
|
||||
u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
void *pKey; /* Saved key that was cursor's last known position */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
#endif
|
||||
i16 iPage; /* Index of current page in apPage */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+19
-17
@@ -726,8 +726,9 @@ void sqlite3StartTable(
|
||||
*/
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
|
||||
if( iDb<0 ) return;
|
||||
if( !OMIT_TEMPDB && isTemp && iDb>1 ){
|
||||
/* If creating a temp table, the name may not be qualified */
|
||||
if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
|
||||
/* If creating a temp table, the name may not be qualified. Unless
|
||||
** the database name is "temp" anyway. */
|
||||
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
|
||||
return;
|
||||
}
|
||||
@@ -775,17 +776,18 @@ void sqlite3StartTable(
|
||||
** collisions.
|
||||
*/
|
||||
if( !IN_DECLARE_VTAB ){
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
|
||||
pTable = sqlite3FindTable(db, zName, zDb);
|
||||
if( pTable ){
|
||||
if( !noErr ){
|
||||
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
|
||||
}
|
||||
goto begin_table_error;
|
||||
}
|
||||
if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
|
||||
if( sqlite3FindIndex(db, zName, zDb)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -802,6 +804,7 @@ void sqlite3StartTable(
|
||||
pTable->iPKey = -1;
|
||||
pTable->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
pTable->nRowEst = 1000000;
|
||||
assert( pParse->pNewTable==0 );
|
||||
pParse->pNewTable = pTable;
|
||||
|
||||
@@ -1648,12 +1651,10 @@ void sqlite3CreateView(
|
||||
}
|
||||
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
|
||||
p = pParse->pNewTable;
|
||||
if( p==0 ){
|
||||
if( p==0 || pParse->nErr ){
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
return;
|
||||
}
|
||||
assert( pParse->nErr==0 ); /* If sqlite3StartTable return non-NULL then
|
||||
** there could not have been an error */
|
||||
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
|
||||
iDb = sqlite3SchemaToIndex(db, p->pSchema);
|
||||
if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
|
||||
@@ -2771,7 +2772,8 @@ Index *sqlite3CreateIndex(
|
||||
sqlite3RefillIndex(pParse, pIndex, iMem);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
|
||||
sqlite3MPrintf(db, "name='%q'", pIndex->zName), P4_DYNAMIC);
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName),
|
||||
P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp1(v, OP_Expire, 0);
|
||||
}
|
||||
}
|
||||
@@ -2832,14 +2834,14 @@ exit_create_index:
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
unsigned *a = pIdx->aiRowEst;
|
||||
int i;
|
||||
unsigned n;
|
||||
assert( a!=0 );
|
||||
a[0] = 1000000;
|
||||
for(i=pIdx->nColumn; i>=5; i--){
|
||||
a[i] = 5;
|
||||
}
|
||||
while( i>=1 ){
|
||||
a[i] = 11 - i;
|
||||
i--;
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
n = 10;
|
||||
for(i=1; i<=pIdx->nColumn; i++){
|
||||
a[i] = n;
|
||||
if( n>5 ) n--;
|
||||
}
|
||||
if( pIdx->onError!=OE_None ){
|
||||
a[pIdx->nColumn] = 1;
|
||||
@@ -2899,7 +2901,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
if( v ){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q",
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
pIndex->zName
|
||||
);
|
||||
@@ -3391,7 +3393,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
SQLITE_OPEN_DELETEONCLOSE |
|
||||
SQLITE_OPEN_TEMP_DB;
|
||||
|
||||
rc = sqlite3BtreeFactory(db, 0, 0, SQLITE_DEFAULT_CACHE_SIZE, flags, &pBt);
|
||||
rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "unable to open a temporary database "
|
||||
"file for storing temporary tables");
|
||||
|
||||
+1
-1
@@ -358,7 +358,7 @@ FuncDef *sqlite3FindFunction(
|
||||
** priority to built-in functions.
|
||||
**
|
||||
** Except, if createFlag is true, that means that we are trying to
|
||||
** install a new function. Whatever FuncDef structure is returned will
|
||||
** install a new function. Whatever FuncDef structure is returned it will
|
||||
** have fields overwritten with new information appropriate for the
|
||||
** new function. But the FuncDefs for built-in functions are read-only.
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
|
||||
@@ -174,6 +174,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
"OMIT_AUTOMATIC_INDEX",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_AUTORESET
|
||||
"OMIT_AUTORESET",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
"OMIT_AUTOVACUUM",
|
||||
#endif
|
||||
|
||||
+10
-14
@@ -133,12 +133,6 @@ end_getDigits:
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read text from z[] and convert into a floating point number. Return
|
||||
** the number of digits converted.
|
||||
*/
|
||||
#define getValue sqlite3AtoF
|
||||
|
||||
/*
|
||||
** Parse a timezone extension on the end of a date-time.
|
||||
** The extension is of the form:
|
||||
@@ -340,7 +334,7 @@ static int parseDateOrTime(
|
||||
const char *zDate,
|
||||
DateTime *p
|
||||
){
|
||||
int isRealNum; /* Return from sqlite3IsNumber(). Not used */
|
||||
double r;
|
||||
if( parseYyyyMmDd(zDate,p)==0 ){
|
||||
return 0;
|
||||
}else if( parseHhMmSs(zDate, p)==0 ){
|
||||
@@ -348,9 +342,7 @@ static int parseDateOrTime(
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0){
|
||||
setDateTimeToCurrent(context, p);
|
||||
return 0;
|
||||
}else if( sqlite3IsNumber(zDate, &isRealNum, SQLITE_UTF8) ){
|
||||
double r;
|
||||
getValue(zDate, &r);
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
||||
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
return 0;
|
||||
@@ -571,8 +563,9 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
|
||||
** date is already on the appropriate weekday, this is a no-op.
|
||||
*/
|
||||
if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
|
||||
&& (n=(int)r)==r && n>=0 && r<7 ){
|
||||
if( strncmp(z, "weekday ", 8)==0
|
||||
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
|
||||
&& (n=(int)r)==r && n>=0 && r<7 ){
|
||||
sqlite3_int64 Z;
|
||||
computeYMD_HMS(p);
|
||||
p->validTZ = 0;
|
||||
@@ -627,8 +620,11 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
case '8':
|
||||
case '9': {
|
||||
double rRounder;
|
||||
n = getValue(z, &r);
|
||||
assert( n>=1 );
|
||||
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
|
||||
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
if( z[n]==':' ){
|
||||
/* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
|
||||
** specified number of hours, minutes, seconds, and fractional seconds
|
||||
|
||||
+63
-98
@@ -484,6 +484,9 @@ Expr *sqlite3PExpr(
|
||||
){
|
||||
Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
|
||||
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
|
||||
if( p ) {
|
||||
sqlite3ExprCheckHeight(pParse, p->nHeight);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -555,7 +558,7 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
|
||||
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
|
||||
** use it as the variable number */
|
||||
i64 i;
|
||||
int bOk = sqlite3Atoi64(&z[1], &i);
|
||||
int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8);
|
||||
pExpr->iColumn = (ynVar)i;
|
||||
testcase( i==0 );
|
||||
testcase( i==1 );
|
||||
@@ -1535,8 +1538,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Generate code for scalar subqueries used as an expression
|
||||
** and IN operators. Examples:
|
||||
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
|
||||
** or IN operators. Examples:
|
||||
**
|
||||
** (SELECT a FROM b) -- subquery
|
||||
** EXISTS (SELECT a FROM b) -- EXISTS subquery
|
||||
@@ -1597,12 +1600,22 @@ int sqlite3CodeSubselect(
|
||||
assert( testAddr>0 || pParse->db->mallocFailed );
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr?"":"CORRELATED ",
|
||||
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_IN: {
|
||||
char affinity;
|
||||
KeyInfo keyInfo;
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
char affinity; /* Affinity of the LHS of the IN */
|
||||
KeyInfo keyInfo; /* Keyinfo for the generated table */
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
|
||||
if( rMayHaveNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
|
||||
@@ -1625,6 +1638,7 @@ int sqlite3CodeSubselect(
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
|
||||
if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
memset(&keyInfo, 0, sizeof(keyInfo));
|
||||
keyInfo.nField = 1;
|
||||
|
||||
@@ -1641,6 +1655,7 @@ int sqlite3CodeSubselect(
|
||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affinity = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
@@ -1741,6 +1756,7 @@ int sqlite3CodeSubselect(
|
||||
sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
||||
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
|
||||
&sqlite3IntTokens[1]);
|
||||
pSel->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pSel, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
@@ -1917,7 +1933,7 @@ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
|
||||
if( ALWAYS(z!=0) ){
|
||||
double value;
|
||||
char *zV;
|
||||
sqlite3AtoF(z, &value);
|
||||
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
|
||||
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
|
||||
if( negateFlag ) value = -value;
|
||||
zV = dup8bytes(v, (char*)&value);
|
||||
@@ -1931,9 +1947,7 @@ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
|
||||
** Generate an instruction that will put the integer describe by
|
||||
** text z[0..n-1] into register iMem.
|
||||
**
|
||||
** The z[] string will probably not be zero-terminated. But the
|
||||
** z[n] character is guaranteed to be something that does not look
|
||||
** like the continuation of the number.
|
||||
** Expr.u.zToken is always UTF8 and zero-terminated.
|
||||
*/
|
||||
static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
@@ -1942,13 +1956,14 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
|
||||
if( negFlag ) i = -i;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
|
||||
}else{
|
||||
int c;
|
||||
i64 value;
|
||||
const char *z = pExpr->u.zToken;
|
||||
assert( z!=0 );
|
||||
if( sqlite3FitsIn64Bits(z, negFlag) ){
|
||||
i64 value;
|
||||
c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
|
||||
if( c==0 || (c==2 && negFlag) ){
|
||||
char *zV;
|
||||
sqlite3Atoi64(z, &value);
|
||||
if( negFlag ) value = -value;
|
||||
if( negFlag ){ value = -value; }
|
||||
zV = dup8bytes(v, (char*)&value);
|
||||
sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
|
||||
}else{
|
||||
@@ -2233,73 +2248,6 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
|
||||
}
|
||||
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
|
||||
|
||||
/*
|
||||
** If the last instruction coded is an ephemeral copy of any of
|
||||
** the registers in the nReg registers beginning with iReg, then
|
||||
** convert the last instruction from OP_SCopy to OP_Copy.
|
||||
*/
|
||||
void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){
|
||||
VdbeOp *pOp;
|
||||
Vdbe *v;
|
||||
|
||||
assert( pParse->db->mallocFailed==0 );
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
pOp = sqlite3VdbeGetOp(v, -1);
|
||||
assert( pOp!=0 );
|
||||
if( pOp->opcode==OP_SCopy && pOp->p1>=iReg && pOp->p1<iReg+nReg ){
|
||||
pOp->opcode = OP_Copy;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to store the value of the iAlias-th alias in register
|
||||
** target. The first time this is called, pExpr is evaluated to compute
|
||||
** the value of the alias. The value is stored in an auxiliary register
|
||||
** and the number of that register is returned. On subsequent calls,
|
||||
** the register number is returned without generating any code.
|
||||
**
|
||||
** Note that in order for this to work, code must be generated in the
|
||||
** same order that it is executed.
|
||||
**
|
||||
** Aliases are numbered starting with 1. So iAlias is in the range
|
||||
** of 1 to pParse->nAlias inclusive.
|
||||
**
|
||||
** pParse->aAlias[iAlias-1] records the register number where the value
|
||||
** of the iAlias-th alias is stored. If zero, that means that the
|
||||
** alias has not yet been computed.
|
||||
*/
|
||||
static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){
|
||||
#if 0
|
||||
sqlite3 *db = pParse->db;
|
||||
int iReg;
|
||||
if( pParse->nAliasAlloc<pParse->nAlias ){
|
||||
pParse->aAlias = sqlite3DbReallocOrFree(db, pParse->aAlias,
|
||||
sizeof(pParse->aAlias[0])*pParse->nAlias );
|
||||
testcase( db->mallocFailed && pParse->nAliasAlloc>0 );
|
||||
if( db->mallocFailed ) return 0;
|
||||
memset(&pParse->aAlias[pParse->nAliasAlloc], 0,
|
||||
(pParse->nAlias-pParse->nAliasAlloc)*sizeof(pParse->aAlias[0]));
|
||||
pParse->nAliasAlloc = pParse->nAlias;
|
||||
}
|
||||
assert( iAlias>0 && iAlias<=pParse->nAlias );
|
||||
iReg = pParse->aAlias[iAlias-1];
|
||||
if( iReg==0 ){
|
||||
if( pParse->iCacheLevel>0 ){
|
||||
iReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
}else{
|
||||
iReg = ++pParse->nMem;
|
||||
sqlite3ExprCode(pParse, pExpr, iReg);
|
||||
pParse->aAlias[iAlias-1] = iReg;
|
||||
}
|
||||
}
|
||||
return iReg;
|
||||
#else
|
||||
UNUSED_PARAMETER(iAlias);
|
||||
return sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code into the current Vdbe to evaluate the given
|
||||
** expression. Attempt to store the results in register "target".
|
||||
@@ -2408,7 +2356,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
break;
|
||||
}
|
||||
case TK_AS: {
|
||||
inReg = codeAlias(pParse, pExpr->iTable, pExpr->pLeft, target);
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CAST
|
||||
@@ -2840,6 +2788,11 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
opCompare.op = TK_EQ;
|
||||
opCompare.pLeft = &cacheX;
|
||||
pTest = &opCompare;
|
||||
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
|
||||
** The value in regFree1 might get SCopy-ed into the file result.
|
||||
** So make sure that the regFree1 register is not reused for other
|
||||
** purposes and possibly overwritten. */
|
||||
regFree1 = 0;
|
||||
}
|
||||
for(i=0; i<nExpr; i=i+2){
|
||||
sqlite3ExprCachePush(pParse);
|
||||
@@ -2933,10 +2886,14 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
int inReg;
|
||||
|
||||
assert( target>0 && target<=pParse->nMem );
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
assert( pParse->pVdbe || pParse->db->mallocFailed );
|
||||
if( inReg!=target && pParse->pVdbe ){
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
|
||||
if( pExpr && pExpr->op==TK_REGISTER ){
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target);
|
||||
}else{
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
assert( pParse->pVdbe || pParse->db->mallocFailed );
|
||||
if( inReg!=target && pParse->pVdbe ){
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
@@ -3083,9 +3040,22 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){
|
||||
** Preevaluate constant subexpressions within pExpr and store the
|
||||
** results in registers. Modify pExpr so that the constant subexpresions
|
||||
** are TK_REGISTER opcodes that refer to the precomputed values.
|
||||
**
|
||||
** This routine is a no-op if the jump to the cookie-check code has
|
||||
** already occur. Since the cookie-check jump is generated prior to
|
||||
** any other serious processing, this check ensures that there is no
|
||||
** way to accidently bypass the constant initializations.
|
||||
**
|
||||
** This routine is also a no-op if the SQLITE_FactorOutConst optimization
|
||||
** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)
|
||||
** interface. This allows test logic to verify that the same answer is
|
||||
** obtained for queries regardless of whether or not constants are
|
||||
** precomputed into registers or if they are inserted in-line.
|
||||
*/
|
||||
void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
|
||||
Walker w;
|
||||
if( pParse->cookieGoto ) return;
|
||||
if( (pParse->db->flags & SQLITE_FactorOutConst)!=0 ) return;
|
||||
w.xExprCallback = evalConstExpr;
|
||||
w.xSelectCallback = 0;
|
||||
w.pParse = pParse;
|
||||
@@ -3109,19 +3079,14 @@ int sqlite3ExprCodeExprList(
|
||||
int i, n;
|
||||
assert( pList!=0 );
|
||||
assert( target>0 );
|
||||
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
|
||||
n = pList->nExpr;
|
||||
for(pItem=pList->a, i=0; i<n; i++, pItem++){
|
||||
if( pItem->iAlias ){
|
||||
int iReg = codeAlias(pParse, pItem->iAlias, pItem->pExpr, target+i);
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( iReg!=target+i ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target+i);
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pItem->pExpr, target+i);
|
||||
}
|
||||
if( doHardCopy && !pParse->db->mallocFailed ){
|
||||
sqlite3ExprHardCopy(pParse, target, n);
|
||||
Expr *pExpr = pItem->pExpr;
|
||||
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
|
||||
if( inReg!=target+i ){
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy,
|
||||
inReg, target+i);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
|
||||
+1
-1
@@ -380,7 +380,7 @@ static void fkLookupParent(
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[i]+1+regData, regTemp+i);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
|
||||
}
|
||||
|
||||
/* If the parent table is the same as the child table, and we are about
|
||||
|
||||
+8
-8
@@ -285,7 +285,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
sqlite3AtoF(zBuf, &r);
|
||||
sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
|
||||
sqlite3_free(zBuf);
|
||||
}
|
||||
sqlite3_result_double(context, r);
|
||||
@@ -1450,10 +1450,10 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
}else{
|
||||
pInfo = (struct compareInfo*)&likeInfoNorm;
|
||||
}
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0,0);
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0, 0, 0);
|
||||
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
||||
setLikeOptFlag(db, "like",
|
||||
caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
|
||||
@@ -1537,10 +1537,10 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */
|
||||
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0},
|
||||
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */
|
||||
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0},
|
||||
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
|
||||
FUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
@@ -1567,7 +1567,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
||||
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
|
||||
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0},
|
||||
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
|
||||
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
||||
@@ -327,6 +327,46 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_next_stmt,
|
||||
sqlite3_sql,
|
||||
sqlite3_status,
|
||||
|
||||
/*
|
||||
** Added for 3.7.4
|
||||
*/
|
||||
sqlite3_backup_finish,
|
||||
sqlite3_backup_init,
|
||||
sqlite3_backup_pagecount,
|
||||
sqlite3_backup_remaining,
|
||||
sqlite3_backup_step,
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
sqlite3_compileoption_get,
|
||||
sqlite3_compileoption_used,
|
||||
#else
|
||||
0,
|
||||
0,
|
||||
#endif
|
||||
sqlite3_create_function_v2,
|
||||
sqlite3_db_config,
|
||||
sqlite3_db_mutex,
|
||||
sqlite3_db_status,
|
||||
sqlite3_extended_errcode,
|
||||
sqlite3_log,
|
||||
sqlite3_soft_heap_limit64,
|
||||
sqlite3_sourceid,
|
||||
sqlite3_stmt_status,
|
||||
sqlite3_strnicmp,
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
sqlite3_unlock_notify,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
sqlite3_wal_autocheckpoint,
|
||||
sqlite3_wal_checkpoint,
|
||||
sqlite3_wal_hook,
|
||||
#else
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+187
-90
@@ -26,15 +26,33 @@
|
||||
# include "sqliteicu.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The version of the library
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
|
||||
** contains the text of SQLITE_VERSION macro.
|
||||
*/
|
||||
const char sqlite3_version[] = SQLITE_VERSION;
|
||||
#endif
|
||||
|
||||
/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns
|
||||
** a pointer to the to the sqlite3_version[] string constant.
|
||||
*/
|
||||
const char *sqlite3_libversion(void){ return sqlite3_version; }
|
||||
|
||||
/* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a
|
||||
** pointer to a string constant whose value is the same as the
|
||||
** SQLITE_SOURCE_ID C preprocessor macro.
|
||||
*/
|
||||
const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
||||
|
||||
/* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function
|
||||
** returns an integer equal to SQLITE_VERSION_NUMBER.
|
||||
*/
|
||||
int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
|
||||
|
||||
/* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns
|
||||
** zero if and only if SQLite was compiled mutexing code omitted due to
|
||||
** the SQLITE_THREADSAFE compile-time option being set to 0.
|
||||
*/
|
||||
int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
|
||||
|
||||
#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
|
||||
@@ -155,6 +173,13 @@ int sqlite3_initialize(void){
|
||||
** sqlite3_initialize(). The recursive calls normally come through
|
||||
** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other
|
||||
** recursive calls might also be possible.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls
|
||||
** to the xInit method, so the xInit method need not be threadsafe.
|
||||
**
|
||||
** The following mutex is what serializes access to the appdef pcache xInit
|
||||
** methods. The sqlite3_pcache_methods.xInit() all is embedded in the
|
||||
** call to sqlite3PcacheInitialize().
|
||||
*/
|
||||
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
|
||||
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
|
||||
@@ -435,12 +460,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
sz = 0;
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sz = ROUND8(sz);
|
||||
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*cnt );
|
||||
pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
|
||||
sqlite3EndBenignMalloc();
|
||||
}else{
|
||||
sz = ROUNDDOWN8(sz);
|
||||
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
|
||||
pStart = pBuf;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
@@ -483,14 +508,14 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_DBCONFIG_LOOKASIDE: {
|
||||
void *pBuf = va_arg(ap, void*);
|
||||
int sz = va_arg(ap, int);
|
||||
int cnt = va_arg(ap, int);
|
||||
void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */
|
||||
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
|
||||
int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */
|
||||
rc = setupLookaside(db, pBuf, sz, cnt);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -595,11 +620,28 @@ void sqlite3CloseSavepoints(sqlite3 *db){
|
||||
db->isTransactionSavepoint = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke the destructor function associated with FuncDef p, if any. Except,
|
||||
** if this is not the last copy of the function, do not invoke it. Multiple
|
||||
** copies of a single function are created when create_function() is called
|
||||
** with SQLITE_ANY as the encoding.
|
||||
*/
|
||||
static void functionDestroy(sqlite3 *db, FuncDef *p){
|
||||
FuncDestructor *pDestructor = p->pDestructor;
|
||||
if( pDestructor ){
|
||||
pDestructor->nRef--;
|
||||
if( pDestructor->nRef==0 ){
|
||||
pDestructor->xDestroy(pDestructor->pUserData);
|
||||
sqlite3DbFree(db, pDestructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an existing SQLite database
|
||||
*/
|
||||
int sqlite3_close(sqlite3 *db){
|
||||
HashElem *i;
|
||||
HashElem *i; /* Hash table iterator */
|
||||
int j;
|
||||
|
||||
if( !db ){
|
||||
@@ -667,6 +709,7 @@ int sqlite3_close(sqlite3 *db){
|
||||
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;
|
||||
@@ -773,7 +816,7 @@ const char *sqlite3ErrStr(int rc){
|
||||
/* SQLITE_INTERRUPT */ "interrupted",
|
||||
/* SQLITE_IOERR */ "disk I/O error",
|
||||
/* SQLITE_CORRUPT */ "database disk image is malformed",
|
||||
/* SQLITE_NOTFOUND */ 0,
|
||||
/* SQLITE_NOTFOUND */ "unknown operation",
|
||||
/* SQLITE_FULL */ "database or disk is full",
|
||||
/* SQLITE_CANTOPEN */ "unable to open database file",
|
||||
/* SQLITE_PROTOCOL */ "locking protocol",
|
||||
@@ -941,7 +984,8 @@ int sqlite3CreateFunc(
|
||||
void *pUserData,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*)
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
FuncDestructor *pDestructor
|
||||
){
|
||||
FuncDef *p;
|
||||
int nName;
|
||||
@@ -969,10 +1013,10 @@ int sqlite3CreateFunc(
|
||||
}else if( enc==SQLITE_ANY ){
|
||||
int rc;
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
|
||||
pUserData, xFunc, xStep, xFinal);
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
|
||||
pUserData, xFunc, xStep, xFinal);
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@@ -1005,6 +1049,15 @@ int sqlite3CreateFunc(
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* If an older version of the function with a configured destructor is
|
||||
** being replaced invoke the destructor function here. */
|
||||
functionDestroy(db, p);
|
||||
|
||||
if( pDestructor ){
|
||||
pDestructor->nRef++;
|
||||
}
|
||||
p->pDestructor = pDestructor;
|
||||
p->flags = 0;
|
||||
p->xFunc = xFunc;
|
||||
p->xStep = xStep;
|
||||
@@ -1019,7 +1072,7 @@ int sqlite3CreateFunc(
|
||||
*/
|
||||
int sqlite3_create_function(
|
||||
sqlite3 *db,
|
||||
const char *zFunctionName,
|
||||
const char *zFunc,
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
@@ -1027,9 +1080,41 @@ int sqlite3_create_function(
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*)
|
||||
){
|
||||
int rc;
|
||||
return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep,
|
||||
xFinal, 0);
|
||||
}
|
||||
|
||||
int sqlite3_create_function_v2(
|
||||
sqlite3 *db,
|
||||
const char *zFunc,
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xDestroy)(void *)
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
FuncDestructor *pArg = 0;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal);
|
||||
if( xDestroy ){
|
||||
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
|
||||
if( !pArg ){
|
||||
xDestroy(p);
|
||||
goto out;
|
||||
}
|
||||
pArg->xDestroy = xDestroy;
|
||||
pArg->pUserData = p;
|
||||
}
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
|
||||
if( pArg && pArg->nRef==0 ){
|
||||
assert( rc!=SQLITE_OK );
|
||||
xDestroy(p);
|
||||
sqlite3DbFree(db, pArg);
|
||||
}
|
||||
|
||||
out:
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
@@ -1051,7 +1136,7 @@ int sqlite3_create_function16(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
|
||||
sqlite3DbFree(db, zFunc8);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@@ -1082,7 +1167,7 @@ int sqlite3_overload_function(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
0, sqlite3InvalidFunction, 0, 0);
|
||||
0, sqlite3InvalidFunction, 0, 0, 0);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, SQLITE_OK);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@@ -1220,7 +1305,10 @@ int sqlite3WalDefaultHook(
|
||||
** configured by this function.
|
||||
*/
|
||||
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER(nFrame);
|
||||
#else
|
||||
if( nFrame>0 ){
|
||||
sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
|
||||
}else{
|
||||
@@ -1350,60 +1438,6 @@ int sqlite3TempInMemory(const sqlite3 *db){
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to create a connection to a database BTree
|
||||
** driver. If zFilename is the name of a file, then that file is
|
||||
** opened and used. If zFilename is the magic name ":memory:" then
|
||||
** the database is stored in memory (and is thus forgotten as soon as
|
||||
** the connection is closed.) If zFilename is NULL then the database
|
||||
** is a "virtual" database for transient use only and is deleted as
|
||||
** soon as the connection is closed.
|
||||
**
|
||||
** A virtual database can be either a disk file (that is automatically
|
||||
** deleted when the file is closed) or it an be held entirely in memory.
|
||||
** The sqlite3TempInMemory() function is used to determine which.
|
||||
*/
|
||||
int sqlite3BtreeFactory(
|
||||
sqlite3 *db, /* Main database when opening aux otherwise 0 */
|
||||
const char *zFilename, /* Name of the file containing the BTree database */
|
||||
int omitJournal, /* if TRUE then do not journal this file */
|
||||
int nCache, /* How many pages in the page cache */
|
||||
int vfsFlags, /* Flags passed through to vfsOpen */
|
||||
Btree **ppBtree /* Pointer to new Btree object written here */
|
||||
){
|
||||
int btFlags = 0;
|
||||
int rc;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( ppBtree != 0);
|
||||
if( omitJournal ){
|
||||
btFlags |= BTREE_OMIT_JOURNAL;
|
||||
}
|
||||
if( db->flags & SQLITE_NoReadlock ){
|
||||
btFlags |= BTREE_NO_READLOCK;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_MEMORYDB
|
||||
if( zFilename==0 && sqlite3TempInMemory(db) ){
|
||||
zFilename = ":memory:";
|
||||
}
|
||||
#endif
|
||||
|
||||
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (zFilename==0 || *zFilename==0) ){
|
||||
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
|
||||
}
|
||||
rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btFlags, vfsFlags);
|
||||
|
||||
/* If the B-Tree was successfully opened, set the pager-cache size to the
|
||||
** default value. Except, if the call to BtreeOpen() returned a handle
|
||||
** open on an existing shared pager-cache, do not change the pager-cache
|
||||
** size.
|
||||
*/
|
||||
if( rc==SQLITE_OK && 0==sqlite3BtreeSchema(*ppBtree, 0, 0) ){
|
||||
sqlite3BtreeSetCacheSize(*ppBtree, nCache);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return UTF-8 encoded English language explanation of the most recent
|
||||
** error.
|
||||
@@ -1568,13 +1602,12 @@ static int createCollation(
|
||||
}
|
||||
|
||||
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1);
|
||||
if( pColl ){
|
||||
pColl->xCmp = xCompare;
|
||||
pColl->pUser = pCtx;
|
||||
pColl->xDel = xDel;
|
||||
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
|
||||
pColl->type = collType;
|
||||
}
|
||||
if( pColl==0 ) return SQLITE_NOMEM;
|
||||
pColl->xCmp = xCompare;
|
||||
pColl->pUser = pCtx;
|
||||
pColl->xDel = xDel;
|
||||
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
|
||||
pColl->type = collType;
|
||||
sqlite3Error(db, SQLITE_OK, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -1646,17 +1679,39 @@ static const int aHardLimit[] = {
|
||||
*/
|
||||
int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
int oldLimit;
|
||||
|
||||
|
||||
/* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME
|
||||
** there is a hard upper bound set at compile-time by a C preprocessor
|
||||
** macro called SQLITE_MAX_NAME. (The "_LIMIT_" in the name is changed to
|
||||
** "_MAX_".)
|
||||
*/
|
||||
assert( aHardLimit[SQLITE_LIMIT_LENGTH]==SQLITE_MAX_LENGTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN );
|
||||
assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT);
|
||||
assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP );
|
||||
assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG );
|
||||
assert( aHardLimit[SQLITE_LIMIT_ATTACHED]==SQLITE_MAX_ATTACHED );
|
||||
assert( aHardLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]==
|
||||
SQLITE_MAX_LIKE_PATTERN_LENGTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
|
||||
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
|
||||
assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) );
|
||||
|
||||
|
||||
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
|
||||
return -1;
|
||||
}
|
||||
oldLimit = db->aLimit[limitId];
|
||||
if( newLimit>=0 ){
|
||||
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
|
||||
if( newLimit>aHardLimit[limitId] ){
|
||||
newLimit = aHardLimit[limitId];
|
||||
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
|
||||
}
|
||||
db->aLimit[limitId] = newLimit;
|
||||
}
|
||||
return oldLimit;
|
||||
return oldLimit; /* IMP: R-53341-35419 */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1680,6 +1735,24 @@ static int openDatabase(
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
|
||||
/* Only allow sensible combinations of bits in the flags argument.
|
||||
** Throw an error if any non-sense combination is used. If we
|
||||
** do not block illegal combinations here, it could trigger
|
||||
** assert() statements in deeper layers. Sensible combinations
|
||||
** are:
|
||||
**
|
||||
** 1: SQLITE_OPEN_READONLY
|
||||
** 2: SQLITE_OPEN_READWRITE
|
||||
** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
|
||||
*/
|
||||
assert( SQLITE_OPEN_READONLY == 0x01 );
|
||||
assert( SQLITE_OPEN_READWRITE == 0x02 );
|
||||
assert( SQLITE_OPEN_CREATE == 0x04 );
|
||||
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
|
||||
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
|
||||
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
|
||||
if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE;
|
||||
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0 ){
|
||||
isThreadsafe = 0;
|
||||
}else if( flags & SQLITE_OPEN_NOMUTEX ){
|
||||
@@ -1713,7 +1786,8 @@ static int openDatabase(
|
||||
SQLITE_OPEN_SUBJOURNAL |
|
||||
SQLITE_OPEN_MASTER_JOURNAL |
|
||||
SQLITE_OPEN_NOMUTEX |
|
||||
SQLITE_OPEN_FULLMUTEX
|
||||
SQLITE_OPEN_FULLMUTEX |
|
||||
SQLITE_OPEN_WAL
|
||||
);
|
||||
|
||||
/* Allocate the sqlite data structure */
|
||||
@@ -1747,6 +1821,9 @@ static int openDatabase(
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS
|
||||
| SQLITE_RecTriggers
|
||||
#endif
|
||||
#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS
|
||||
| SQLITE_ForeignKeys
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
@@ -1785,9 +1862,8 @@ static int openDatabase(
|
||||
|
||||
/* Open the backend database driver */
|
||||
db->openFlags = flags;
|
||||
rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE,
|
||||
flags | SQLITE_OPEN_MAIN_DB,
|
||||
&db->aDb[0].pBt);
|
||||
rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0,
|
||||
flags | SQLITE_OPEN_MAIN_DB);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_IOERR_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -2283,8 +2359,13 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
|
||||
assert( pPager!=0 );
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
assert( fd!=0 );
|
||||
if( fd->pMethods ){
|
||||
if( op==SQLITE_FCNTL_FILE_POINTER ){
|
||||
*(sqlite3_file**)pArg = fd;
|
||||
rc = SQLITE_OK;
|
||||
}else if( fd->pMethods ){
|
||||
rc = sqlite3OsFileControl(fd, op, pArg);
|
||||
}else{
|
||||
rc = SQLITE_NOTFOUND;
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
@@ -2494,6 +2575,22 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
|
||||
**
|
||||
** Pass pFree into sqlite3ScratchFree().
|
||||
** If sz>0 then allocate a scratch buffer into pNew.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_SCRATCHMALLOC: {
|
||||
void *pFree, **ppNew;
|
||||
int sz;
|
||||
sz = va_arg(ap, int);
|
||||
ppNew = va_arg(ap, void**);
|
||||
pFree = va_arg(ap, void*);
|
||||
if( sz ) *ppNew = sqlite3ScratchMalloc(sz);
|
||||
sqlite3ScratchFree(pFree);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+215
-184
@@ -15,6 +15,66 @@
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
** Attempt to release up to n bytes of non-essential memory currently
|
||||
** held by SQLite. An example of non-essential memory is memory used to
|
||||
** cache database pages that are not currently in use.
|
||||
*/
|
||||
int sqlite3_release_memory(int n){
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
return sqlite3PcacheReleaseMemory(n);
|
||||
#else
|
||||
/* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine
|
||||
** is a no-op returning zero if SQLite is not compiled with
|
||||
** SQLITE_ENABLE_MEMORY_MANAGEMENT. */
|
||||
UNUSED_PARAMETER(n);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** An instance of the following object records the location of
|
||||
** each unused scratch buffer.
|
||||
*/
|
||||
typedef struct ScratchFreeslot {
|
||||
struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
|
||||
} ScratchFreeslot;
|
||||
|
||||
/*
|
||||
** State information local to the memory allocation subsystem.
|
||||
*/
|
||||
static SQLITE_WSD struct Mem0Global {
|
||||
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
||||
|
||||
/*
|
||||
** The alarm callback and its arguments. The mem0.mutex lock will
|
||||
** be held while the callback is running. Recursive calls into
|
||||
** the memory subsystem are allowed, but no new callbacks will be
|
||||
** issued.
|
||||
*/
|
||||
sqlite3_int64 alarmThreshold;
|
||||
void (*alarmCallback)(void*, sqlite3_int64,int);
|
||||
void *alarmArg;
|
||||
|
||||
/*
|
||||
** Pointers to the end of sqlite3GlobalConfig.pScratch memory
|
||||
** (so that a range test can be used to determine if an allocation
|
||||
** being freed came from pScratch) and a pointer to the list of
|
||||
** unused scratch allocations.
|
||||
*/
|
||||
void *pScratchEnd;
|
||||
ScratchFreeslot *pScratchFree;
|
||||
u32 nScratchFree;
|
||||
|
||||
/*
|
||||
** True if heap is nearly "full" where "full" is defined by the
|
||||
** sqlite3_soft_heap_limit() setting.
|
||||
*/
|
||||
int nearlyFull;
|
||||
} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
||||
|
||||
/*
|
||||
** This routine runs when the memory allocator sees that the
|
||||
** total memory allocation is about to exceed the soft heap
|
||||
@@ -29,79 +89,67 @@ static void softHeapLimitEnforcer(
|
||||
sqlite3_release_memory(allocSize);
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the alarm callback
|
||||
*/
|
||||
static int sqlite3MemoryAlarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
int nUsed;
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
mem0.alarmCallback = xCallback;
|
||||
mem0.alarmArg = pArg;
|
||||
mem0.alarmThreshold = iThreshold;
|
||||
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Deprecated external interface. Internal/core SQLite code
|
||||
** should call sqlite3MemoryAlarm.
|
||||
*/
|
||||
int sqlite3_memory_alarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Set the soft heap-size limit for the library. Passing a zero or
|
||||
** negative value indicates no limit.
|
||||
*/
|
||||
void sqlite3_soft_heap_limit(int n){
|
||||
sqlite3_uint64 iLimit;
|
||||
int overage;
|
||||
if( n<0 ){
|
||||
iLimit = 0;
|
||||
}else{
|
||||
iLimit = n;
|
||||
}
|
||||
sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
||||
sqlite3_int64 priorLimit;
|
||||
sqlite3_int64 excess;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
sqlite3_initialize();
|
||||
#endif
|
||||
if( iLimit>0 ){
|
||||
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, iLimit);
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
priorLimit = mem0.alarmThreshold;
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
if( n<0 ) return priorLimit;
|
||||
if( n>0 ){
|
||||
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
|
||||
}else{
|
||||
sqlite3MemoryAlarm(0, 0, 0);
|
||||
}
|
||||
overage = (int)(sqlite3_memory_used() - (i64)n);
|
||||
if( overage>0 ){
|
||||
sqlite3_release_memory(overage);
|
||||
}
|
||||
excess = sqlite3_memory_used() - n;
|
||||
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
||||
return priorLimit;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to release up to n bytes of non-essential memory currently
|
||||
** held by SQLite. An example of non-essential memory is memory used to
|
||||
** cache database pages that are not currently in use.
|
||||
*/
|
||||
int sqlite3_release_memory(int n){
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int nRet = 0;
|
||||
nRet += sqlite3PcacheReleaseMemory(n-nRet);
|
||||
return nRet;
|
||||
#else
|
||||
UNUSED_PARAMETER(n);
|
||||
return SQLITE_OK;
|
||||
#endif
|
||||
void sqlite3_soft_heap_limit(int n){
|
||||
if( n<0 ) n = 0;
|
||||
sqlite3_soft_heap_limit64(n);
|
||||
}
|
||||
|
||||
/*
|
||||
** State information local to the memory allocation subsystem.
|
||||
*/
|
||||
static SQLITE_WSD struct Mem0Global {
|
||||
/* Number of free pages for scratch and page-cache memory */
|
||||
u32 nScratchFree;
|
||||
u32 nPageFree;
|
||||
|
||||
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
||||
|
||||
/*
|
||||
** The alarm callback and its arguments. The mem0.mutex lock will
|
||||
** be held while the callback is running. Recursive calls into
|
||||
** the memory subsystem are allowed, but no new callbacks will be
|
||||
** issued.
|
||||
*/
|
||||
sqlite3_int64 alarmThreshold;
|
||||
void (*alarmCallback)(void*, sqlite3_int64,int);
|
||||
void *alarmArg;
|
||||
|
||||
/*
|
||||
** Pointers to the end of sqlite3GlobalConfig.pScratch and
|
||||
** sqlite3GlobalConfig.pPage to a block of memory that records
|
||||
** which pages are available.
|
||||
*/
|
||||
u32 *aScratchFree;
|
||||
u32 *aPageFree;
|
||||
} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
||||
|
||||
/*
|
||||
** Initialize the memory allocation subsystem.
|
||||
*/
|
||||
@@ -114,36 +162,45 @@ int sqlite3MallocInit(void){
|
||||
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
||||
&& sqlite3GlobalConfig.nScratch>=0 ){
|
||||
int i;
|
||||
sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4);
|
||||
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
|
||||
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
|
||||
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
|
||||
mem0.nScratchFree = sqlite3GlobalConfig.nScratch;
|
||||
&& sqlite3GlobalConfig.nScratch>0 ){
|
||||
int i, n, sz;
|
||||
ScratchFreeslot *pSlot;
|
||||
sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
|
||||
sqlite3GlobalConfig.szScratch = sz;
|
||||
pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
|
||||
n = sqlite3GlobalConfig.nScratch;
|
||||
mem0.pScratchFree = pSlot;
|
||||
mem0.nScratchFree = n;
|
||||
for(i=0; i<n-1; i++){
|
||||
pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
|
||||
pSlot = pSlot->pNext;
|
||||
}
|
||||
pSlot->pNext = 0;
|
||||
mem0.pScratchEnd = (void*)&pSlot[1];
|
||||
}else{
|
||||
mem0.pScratchEnd = 0;
|
||||
sqlite3GlobalConfig.pScratch = 0;
|
||||
sqlite3GlobalConfig.szScratch = 0;
|
||||
sqlite3GlobalConfig.nScratch = 0;
|
||||
}
|
||||
if( sqlite3GlobalConfig.pPage && sqlite3GlobalConfig.szPage>=512
|
||||
&& sqlite3GlobalConfig.nPage>=1 ){
|
||||
int i;
|
||||
int overhead;
|
||||
int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage);
|
||||
int n = sqlite3GlobalConfig.nPage;
|
||||
overhead = (4*n + sz - 1)/sz;
|
||||
sqlite3GlobalConfig.nPage -= overhead;
|
||||
mem0.aPageFree = (u32*)&((char*)sqlite3GlobalConfig.pPage)
|
||||
[sqlite3GlobalConfig.szPage*sqlite3GlobalConfig.nPage];
|
||||
for(i=0; i<sqlite3GlobalConfig.nPage; i++){ mem0.aPageFree[i] = i; }
|
||||
mem0.nPageFree = sqlite3GlobalConfig.nPage;
|
||||
}else{
|
||||
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|
||||
|| sqlite3GlobalConfig.nPage<1 ){
|
||||
sqlite3GlobalConfig.pPage = 0;
|
||||
sqlite3GlobalConfig.szPage = 0;
|
||||
sqlite3GlobalConfig.nPage = 0;
|
||||
}
|
||||
return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the heap is currently under memory pressure - in other
|
||||
** words if the amount of heap used is close to the limit set by
|
||||
** sqlite3_soft_heap_limit().
|
||||
*/
|
||||
int sqlite3HeapNearlyFull(void){
|
||||
return mem0.nearlyFull;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deinitialize the memory allocation subsystem.
|
||||
*/
|
||||
@@ -178,36 +235,6 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the alarm callback
|
||||
*/
|
||||
int sqlite3MemoryAlarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
mem0.alarmCallback = xCallback;
|
||||
mem0.alarmArg = pArg;
|
||||
mem0.alarmThreshold = iThreshold;
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Deprecated external interface. Internal/core SQLite code
|
||||
** should call sqlite3MemoryAlarm.
|
||||
*/
|
||||
int sqlite3_memory_alarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Trigger the alarm
|
||||
*/
|
||||
@@ -240,14 +267,19 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
if( mem0.alarmCallback!=0 ){
|
||||
int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
if( nUsed+nFull >= mem0.alarmThreshold ){
|
||||
mem0.nearlyFull = 1;
|
||||
sqlite3MallocAlarm(nFull);
|
||||
}else{
|
||||
mem0.nearlyFull = 0;
|
||||
}
|
||||
}
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
if( p==0 && mem0.alarmCallback ){
|
||||
sqlite3MallocAlarm(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
}
|
||||
#endif
|
||||
if( p ){
|
||||
nFull = sqlite3MallocSize(p);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
|
||||
@@ -263,7 +295,9 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
*/
|
||||
void *sqlite3Malloc(int n){
|
||||
void *p;
|
||||
if( n<=0 || n>=0x7fffff00 ){
|
||||
if( n<=0 /* IMP: R-65312-04917 */
|
||||
|| n>=0x7fffff00
|
||||
){
|
||||
/* A memory allocation of a number of bytes which is near the maximum
|
||||
** signed integer value might cause an integer overflow inside of the
|
||||
** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
|
||||
@@ -277,6 +311,7 @@ void *sqlite3Malloc(int n){
|
||||
}else{
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -315,59 +350,65 @@ void *sqlite3ScratchMalloc(int n){
|
||||
void *p;
|
||||
assert( n>0 );
|
||||
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
||||
p = mem0.pScratchFree;
|
||||
mem0.pScratchFree = mem0.pScratchFree->pNext;
|
||||
mem0.nScratchFree--;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
n = mallocWithAlarm(n, &p);
|
||||
if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
||||
}
|
||||
assert( sqlite3_mutex_notheld(mem0.mutex) );
|
||||
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* Verify that no more than two scratch allocation per thread
|
||||
** is outstanding at one time. (This is only checked in the
|
||||
/* Verify that no more than two scratch allocations per thread
|
||||
** are outstanding at one time. (This is only checked in the
|
||||
** single-threaded case since checking in the multi-threaded case
|
||||
** would be much more complicated.) */
|
||||
assert( scratchAllocOut<=1 );
|
||||
#endif
|
||||
|
||||
if( sqlite3GlobalConfig.szScratch<n ){
|
||||
goto scratch_overflow;
|
||||
}else{
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
if( mem0.nScratchFree==0 ){
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
goto scratch_overflow;
|
||||
}else{
|
||||
int i;
|
||||
i = mem0.aScratchFree[--mem0.nScratchFree];
|
||||
i *= sqlite3GlobalConfig.szScratch;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
p = (void*)&((char*)sqlite3GlobalConfig.pScratch)[i];
|
||||
assert( (((u8*)p - (u8*)0) & 7)==0 );
|
||||
}
|
||||
}
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
scratchAllocOut = p!=0;
|
||||
if( p ) scratchAllocOut++;
|
||||
#endif
|
||||
|
||||
return p;
|
||||
|
||||
scratch_overflow:
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
n = mallocWithAlarm(n, &p);
|
||||
if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
scratchAllocOut = p!=0;
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
void sqlite3ScratchFree(void *p){
|
||||
if( p ){
|
||||
if( sqlite3GlobalConfig.pScratch==0
|
||||
|| p<sqlite3GlobalConfig.pScratch
|
||||
|| p>=(void*)mem0.aScratchFree ){
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* Verify that no more than two scratch allocation per thread
|
||||
** is outstanding at one time. (This is only checked in the
|
||||
** single-threaded case since checking in the multi-threaded case
|
||||
** would be much more complicated.) */
|
||||
assert( scratchAllocOut>=1 && scratchAllocOut<=2 );
|
||||
scratchAllocOut--;
|
||||
#endif
|
||||
|
||||
if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){
|
||||
/* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
|
||||
ScratchFreeslot *pSlot;
|
||||
pSlot = (ScratchFreeslot*)p;
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
pSlot->pNext = mem0.pScratchFree;
|
||||
mem0.pScratchFree = pSlot;
|
||||
mem0.nScratchFree++;
|
||||
assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch );
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
/* Release memory back to the heap */
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
|
||||
assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
@@ -382,26 +423,6 @@ void sqlite3ScratchFree(void *p){
|
||||
}else{
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}else{
|
||||
int i;
|
||||
i = (int)((u8*)p - (u8*)sqlite3GlobalConfig.pScratch);
|
||||
i /= sqlite3GlobalConfig.szScratch;
|
||||
assert( i>=0 && i<sqlite3GlobalConfig.nScratch );
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
assert( mem0.nScratchFree<(u32)sqlite3GlobalConfig.nScratch );
|
||||
mem0.aScratchFree[mem0.nScratchFree++] = i;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* Verify that no more than two scratch allocation per thread
|
||||
** is outstanding at one time. (This is only checked in the
|
||||
** single-threaded case since checking in the multi-threaded case
|
||||
** would be much more complicated.) */
|
||||
assert( scratchAllocOut>=1 && scratchAllocOut<=2 );
|
||||
scratchAllocOut = 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,7 +463,7 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
** Free memory previously obtained from sqlite3Malloc().
|
||||
*/
|
||||
void sqlite3_free(void *p){
|
||||
if( p==0 ) return;
|
||||
if( p==0 ) return; /* IMP: R-49053-54554 */
|
||||
assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
@@ -489,10 +510,10 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
int nOld, nNew;
|
||||
void *pNew;
|
||||
if( pOld==0 ){
|
||||
return sqlite3Malloc(nBytes);
|
||||
return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
|
||||
}
|
||||
if( nBytes<=0 ){
|
||||
sqlite3_free(pOld);
|
||||
sqlite3_free(pOld); /* IMP: R-31593-10574 */
|
||||
return 0;
|
||||
}
|
||||
if( nBytes>=0x7fffff00 ){
|
||||
@@ -500,6 +521,9 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
return 0;
|
||||
}
|
||||
nOld = sqlite3MallocSize(pOld);
|
||||
/* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
|
||||
** argument to xRealloc is always a value returned by a prior call to
|
||||
** xRoundup. */
|
||||
nNew = sqlite3GlobalConfig.m.xRoundup(nBytes);
|
||||
if( nOld==nNew ){
|
||||
pNew = pOld;
|
||||
@@ -525,6 +549,7 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
}else{
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
}
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */
|
||||
return pNew;
|
||||
}
|
||||
|
||||
@@ -591,14 +616,20 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
if( db->mallocFailed ){
|
||||
return 0;
|
||||
}
|
||||
if( db->lookaside.bEnabled && n<=db->lookaside.sz
|
||||
&& (pBuf = db->lookaside.pFree)!=0 ){
|
||||
db->lookaside.pFree = pBuf->pNext;
|
||||
db->lookaside.nOut++;
|
||||
if( db->lookaside.nOut>db->lookaside.mxOut ){
|
||||
db->lookaside.mxOut = db->lookaside.nOut;
|
||||
if( db->lookaside.bEnabled ){
|
||||
if( n>db->lookaside.sz ){
|
||||
db->lookaside.anStat[1]++;
|
||||
}else if( (pBuf = db->lookaside.pFree)==0 ){
|
||||
db->lookaside.anStat[2]++;
|
||||
}else{
|
||||
db->lookaside.pFree = pBuf->pNext;
|
||||
db->lookaside.nOut++;
|
||||
db->lookaside.anStat[0]++;
|
||||
if( db->lookaside.nOut>db->lookaside.mxOut ){
|
||||
db->lookaside.mxOut = db->lookaside.nOut;
|
||||
}
|
||||
return (void*)pBuf;
|
||||
}
|
||||
return (void*)pBuf;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ static int sqlite3MemSize(void *pPrior){
|
||||
static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
||||
assert( pPrior!=0 && nByte>0 );
|
||||
nByte = ROUND8(nByte);
|
||||
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
|
||||
p--;
|
||||
p = realloc(p, nByte+8 );
|
||||
if( p ){
|
||||
|
||||
@@ -344,6 +344,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
struct MemBlockHdr *pOldHdr;
|
||||
void *pNew;
|
||||
assert( mem.disallow==0 );
|
||||
assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */
|
||||
pOldHdr = sqlite3MemsysGetHeader(pPrior);
|
||||
pNew = sqlite3MemMalloc(nByte);
|
||||
if( pNew ){
|
||||
|
||||
+1
-1
@@ -395,7 +395,7 @@ static void *memsys5Realloc(void *pPrior, int nBytes){
|
||||
int nOld;
|
||||
void *p;
|
||||
assert( pPrior!=0 );
|
||||
assert( (nBytes&(nBytes-1))==0 );
|
||||
assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */
|
||||
assert( nBytes>=0 );
|
||||
if( nBytes==0 ){
|
||||
return 0;
|
||||
|
||||
+1
-2
@@ -252,8 +252,7 @@ int sqlite3IsMemJournal(sqlite3_file *pJfd){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a MemJournal that uses vfs
|
||||
** pVfs to create the underlying on-disk files.
|
||||
** Return the number of bytes required to store a MemJournal file descriptor.
|
||||
*/
|
||||
int sqlite3MemJournalSize(void){
|
||||
return sizeof(MemJournal);
|
||||
|
||||
+2
-2
@@ -63,8 +63,8 @@
|
||||
#define sqlite3_mutex_enter(X)
|
||||
#define sqlite3_mutex_try(X) SQLITE_OK
|
||||
#define sqlite3_mutex_leave(X)
|
||||
#define sqlite3_mutex_held(X) 1
|
||||
#define sqlite3_mutex_notheld(X) 1
|
||||
#define sqlite3_mutex_held(X) ((void)(X),1)
|
||||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
|
||||
#define sqlite3MutexInit() SQLITE_OK
|
||||
#define sqlite3MutexEnd()
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU2
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ static int winMutexEnd(void){
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU2
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
|
||||
@@ -183,6 +183,12 @@ int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
}
|
||||
int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
|
||||
int rc;
|
||||
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
|
||||
** method to get the current date and time if that method is available
|
||||
** (if iVersion is 2 or greater and the function pointer is not NULL) and
|
||||
** will fall back to xCurrentTime() if xCurrentTimeInt64() is
|
||||
** unavailable.
|
||||
*/
|
||||
if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
|
||||
rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut);
|
||||
}else{
|
||||
|
||||
+1
-1
@@ -533,7 +533,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+52
-18
@@ -119,7 +119,9 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
# include <sys/ioctl.h>
|
||||
@@ -3133,8 +3135,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
return proxyFileControl(id,op,pArg);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: {
|
||||
return SQLITE_OK; /* A no-op */
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3559,7 +3564,7 @@ static int unixShmMap(
|
||||
pShmNode->apRegion = apNew;
|
||||
while(pShmNode->nRegion<=iRegion){
|
||||
void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, pShmNode->h, iRegion*szRegion
|
||||
MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
|
||||
);
|
||||
if( pMem==MAP_FAILED ){
|
||||
rc = SQLITE_IOERR;
|
||||
@@ -4077,11 +4082,21 @@ static int fillInUnixFile(
|
||||
*/
|
||||
UNUSED_PARAMETER(isDelete);
|
||||
|
||||
/* Usually the path zFilename should not be a relative pathname. The
|
||||
** exception is when opening the proxy "conch" file in builds that
|
||||
** include the special Apple locking styles.
|
||||
*/
|
||||
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
||||
assert( zFilename==0 || zFilename[0]=='/'
|
||||
|| pVfs->pAppData==(void*)&autolockIoFinder );
|
||||
#else
|
||||
assert( zFilename==0 || zFilename[0]=='/' );
|
||||
#endif
|
||||
|
||||
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
||||
pNew->h = h;
|
||||
pNew->dirfd = dirfd;
|
||||
pNew->fileFlags = 0;
|
||||
assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */
|
||||
pNew->zPath = zFilename;
|
||||
|
||||
#if OS_VXWORKS
|
||||
@@ -4421,9 +4436,24 @@ static int findCreateFileMode(
|
||||
int nDb; /* Number of valid bytes in zDb */
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
|
||||
nDb = sqlite3Strlen30(zPath) - ((flags & SQLITE_OPEN_WAL) ? 4 : 8);
|
||||
/* 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
|
||||
** the following naming conventions:
|
||||
**
|
||||
** "<path to db>-journal"
|
||||
** "<path to db>-wal"
|
||||
** "<path to db>-journal-NNNN"
|
||||
** "<path to db>-wal-NNNN"
|
||||
**
|
||||
** where NNNN is a 4 digit decimal number. The NNNN naming schemes are
|
||||
** used by the test_multiplex.c module.
|
||||
*/
|
||||
nDb = sqlite3Strlen30(zPath) - 1;
|
||||
while( nDb>0 && zPath[nDb]!='l' ) nDb--;
|
||||
nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7);
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
if( 0==stat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
}else{
|
||||
@@ -4838,7 +4868,7 @@ static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
|
||||
** error message.
|
||||
*/
|
||||
static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){
|
||||
char *zErr;
|
||||
const char *zErr;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
unixEnterMutex();
|
||||
zErr = dlerror();
|
||||
@@ -4975,7 +5005,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
||||
#if defined(NO_GETTOD)
|
||||
time_t t;
|
||||
time(&t);
|
||||
*piNow = ((sqlite3_int64)i)*1000 + unixEpoch;
|
||||
*piNow = ((sqlite3_int64)t)*1000 + unixEpoch;
|
||||
#elif OS_VXWORKS
|
||||
struct timespec sNow;
|
||||
clock_gettime(CLOCK_REALTIME, &sNow);
|
||||
@@ -5378,17 +5408,21 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
||||
** bytes of writable memory.
|
||||
*/
|
||||
static int proxyGetHostID(unsigned char *pHostID, int *pError){
|
||||
struct timespec timeout = {1, 0}; /* 1 sec timeout */
|
||||
|
||||
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
|
||||
memset(pHostID, 0, PROXY_HOSTIDLEN);
|
||||
if( gethostuuid(pHostID, &timeout) ){
|
||||
int err = errno;
|
||||
if( pError ){
|
||||
*pError = err;
|
||||
#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\
|
||||
&& __MAC_OS_X_VERSION_MIN_REQUIRED<1050
|
||||
{
|
||||
static const struct timespec timeout = {1, 0}; /* 1 sec timeout */
|
||||
if( gethostuuid(pHostID, &timeout) ){
|
||||
int err = errno;
|
||||
if( pError ){
|
||||
*pError = err;
|
||||
}
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
/* simulate multiple hosts by creating unique hostid file paths */
|
||||
if( sqlite3_hostid_num != 0){
|
||||
@@ -5429,27 +5463,27 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
||||
pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
|
||||
if( pathLen>MAXPATHLEN || pathLen<6 ||
|
||||
(strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
|
||||
sprintf(errmsg, "path error (len %d)", (int)pathLen);
|
||||
sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen);
|
||||
goto end_breaklock;
|
||||
}
|
||||
/* read the conch content */
|
||||
readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0);
|
||||
if( readLen<PROXY_PATHINDEX ){
|
||||
sprintf(errmsg, "read error (len %d)", (int)readLen);
|
||||
sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen);
|
||||
goto end_breaklock;
|
||||
}
|
||||
/* write it out to the temporary break file */
|
||||
fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
if( fd<0 ){
|
||||
sprintf(errmsg, "create failed (%d)", errno);
|
||||
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
|
||||
goto end_breaklock;
|
||||
}
|
||||
if( pwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){
|
||||
sprintf(errmsg, "write failed (%d)", errno);
|
||||
sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno);
|
||||
goto end_breaklock;
|
||||
}
|
||||
if( rename(tPath, cPath) ){
|
||||
sprintf(errmsg, "rename failed (%d)", errno);
|
||||
sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno);
|
||||
goto end_breaklock;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
+117
-21
@@ -1183,8 +1183,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
SimulateIOErrorBenign(0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1212,6 +1215,14 @@ static int winDeviceCharacteristics(sqlite3_file *id){
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
|
||||
/*
|
||||
** Windows will only let you create file view mappings
|
||||
** on allocation size granularity boundaries.
|
||||
** During sqlite3_os_init() we do a GetSystemInfo()
|
||||
** to get the granularity size.
|
||||
*/
|
||||
SYSTEM_INFO winSysInfo;
|
||||
|
||||
/*
|
||||
** Helper functions to obtain and relinquish the global mutex. The
|
||||
** global mutex is used to protect the winLockInfo objects used by
|
||||
@@ -1380,6 +1391,7 @@ static int winDelete(sqlite3_vfs *,const char*,int);
|
||||
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
|
||||
winShmNode **pp;
|
||||
winShmNode *p;
|
||||
BOOL bRc;
|
||||
assert( winShmMutexHeld() );
|
||||
pp = &winShmNodeList;
|
||||
while( (p = *pp)!=0 ){
|
||||
@@ -1387,8 +1399,14 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
|
||||
int i;
|
||||
if( p->mutex ) sqlite3_mutex_free(p->mutex);
|
||||
for(i=0; i<p->nRegion; i++){
|
||||
UnmapViewOfFile(p->aRegion[i].pMap);
|
||||
CloseHandle(p->aRegion[i].hMap);
|
||||
bRc = UnmapViewOfFile(p->aRegion[i].pMap);
|
||||
OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
|
||||
(int)GetCurrentProcessId(), i,
|
||||
bRc ? "ok" : "failed"));
|
||||
bRc = CloseHandle(p->aRegion[i].hMap);
|
||||
OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n",
|
||||
(int)GetCurrentProcessId(), i,
|
||||
bRc ? "ok" : "failed"));
|
||||
}
|
||||
if( p->hFile.h != INVALID_HANDLE_VALUE ){
|
||||
SimulateIOErrorBenign(1);
|
||||
@@ -1465,10 +1483,11 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto shm_open_err;
|
||||
}
|
||||
|
||||
rc = winOpen(pDbFd->pVfs,
|
||||
pShmNode->zFilename, /* Name of the file (UTF-8) */
|
||||
(sqlite3_file*)&pShmNode->hFile, /* File handle here */
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
|
||||
SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
|
||||
0);
|
||||
if( SQLITE_OK!=rc ){
|
||||
rc = SQLITE_CANTOPEN_BKPT;
|
||||
@@ -1776,10 +1795,18 @@ static int winShmMap(
|
||||
hMap = CreateFileMapping(pShmNode->hFile.h,
|
||||
NULL, PAGE_READWRITE, 0, nByte, NULL
|
||||
);
|
||||
OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n",
|
||||
(int)GetCurrentProcessId(), pShmNode->nRegion, nByte,
|
||||
hMap ? "ok" : "failed"));
|
||||
if( hMap ){
|
||||
int iOffset = pShmNode->nRegion*szRegion;
|
||||
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
||||
pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
||||
0, 0, nByte
|
||||
0, iOffset - iOffsetShift, szRegion + iOffsetShift
|
||||
);
|
||||
OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n",
|
||||
(int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion,
|
||||
pMap ? "ok" : "failed"));
|
||||
}
|
||||
if( !pMap ){
|
||||
pShmNode->lastErrno = GetLastError();
|
||||
@@ -1796,8 +1823,10 @@ static int winShmMap(
|
||||
|
||||
shmpage_out:
|
||||
if( pShmNode->nRegion>iRegion ){
|
||||
int iOffset = iRegion*szRegion;
|
||||
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
||||
char *p = (char *)pShmNode->aRegion[iRegion].pMap;
|
||||
*pp = (void *)&p[iRegion*szRegion];
|
||||
*pp = (void *)&p[iOffsetShift];
|
||||
}else{
|
||||
*pp = 0;
|
||||
}
|
||||
@@ -2024,9 +2053,60 @@ static int winOpen(
|
||||
int isTemp = 0;
|
||||
#endif
|
||||
winFile *pFile = (winFile*)id;
|
||||
void *zConverted; /* Filename in OS encoding */
|
||||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
|
||||
void *zConverted; /* Filename in OS encoding */
|
||||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
|
||||
/* If argument zPath is a NULL pointer, this function is required to open
|
||||
** a temporary file. Use this buffer to store the file name in.
|
||||
*/
|
||||
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
|
||||
|
||||
int rc = SQLITE_OK; /* Function Return Code */
|
||||
#if !defined(NDEBUG) || SQLITE_OS_WINCE
|
||||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||
#endif
|
||||
|
||||
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
|
||||
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
|
||||
int isCreate = (flags & SQLITE_OPEN_CREATE);
|
||||
#ifndef NDEBUG
|
||||
int isReadonly = (flags & SQLITE_OPEN_READONLY);
|
||||
#endif
|
||||
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
|
||||
|
||||
#ifndef NDEBUG
|
||||
int isOpenJournal = (isCreate && (
|
||||
eType==SQLITE_OPEN_MASTER_JOURNAL
|
||||
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|
||||
|| eType==SQLITE_OPEN_WAL
|
||||
));
|
||||
#endif
|
||||
|
||||
/* Check the following statements are true:
|
||||
**
|
||||
** (a) Exactly one of the READWRITE and READONLY flags must be set, and
|
||||
** (b) if CREATE is set, then READWRITE must also be set, and
|
||||
** (c) if EXCLUSIVE is set, then CREATE must also be set.
|
||||
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
|
||||
*/
|
||||
assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
|
||||
assert(isCreate==0 || isReadWrite);
|
||||
assert(isExclusive==0 || isCreate);
|
||||
assert(isDelete==0 || isCreate);
|
||||
|
||||
/* The main DB, main journal, WAL file and master journal are never
|
||||
** automatically deleted. Nor are they ever temporary files. */
|
||||
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
|
||||
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
|
||||
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
|
||||
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
|
||||
|
||||
/* Assert that the upper layer has set one of the "file-type" flags. */
|
||||
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|
||||
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|
||||
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|
||||
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
|
||||
);
|
||||
|
||||
assert( id!=0 );
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
@@ -2037,7 +2117,8 @@ static int winOpen(
|
||||
** temporary file name to use
|
||||
*/
|
||||
if( !zUtf8Name ){
|
||||
int rc = getTempname(MAX_PATH+1, zTmpname);
|
||||
assert(isDelete && !isOpenJournal);
|
||||
rc = getTempname(MAX_PATH+1, zTmpname);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@@ -2050,29 +2131,31 @@ static int winOpen(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
if( isReadWrite ){
|
||||
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
}else{
|
||||
dwDesiredAccess = GENERIC_READ;
|
||||
}
|
||||
|
||||
/* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
|
||||
** created. SQLite doesn't use it to indicate "exclusive access"
|
||||
** as it is usually understood.
|
||||
*/
|
||||
assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE));
|
||||
if( flags & SQLITE_OPEN_EXCLUSIVE ){
|
||||
if( isExclusive ){
|
||||
/* Creates a new file, only if it does not already exist. */
|
||||
/* If the file exists, it fails. */
|
||||
dwCreationDisposition = CREATE_NEW;
|
||||
}else if( flags & SQLITE_OPEN_CREATE ){
|
||||
}else if( isCreate ){
|
||||
/* Open existing file, or create if it doesn't exist */
|
||||
dwCreationDisposition = OPEN_ALWAYS;
|
||||
}else{
|
||||
/* Opens a file, only if it exists. */
|
||||
dwCreationDisposition = OPEN_EXISTING;
|
||||
}
|
||||
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
|
||||
if( isDelete ){
|
||||
#if SQLITE_OS_WINCE
|
||||
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||||
isTemp = 1;
|
||||
@@ -2089,6 +2172,7 @@ static int winOpen(
|
||||
#if SQLITE_OS_WINCE
|
||||
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
|
||||
#endif
|
||||
|
||||
if( isNT() ){
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
dwDesiredAccess,
|
||||
@@ -2114,26 +2198,30 @@ static int winOpen(
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
OSTRACE(("OPEN %d %s 0x%lx %s\n",
|
||||
h, zName, dwDesiredAccess,
|
||||
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
|
||||
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
free(zConverted);
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
if( isReadWrite ){
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
|
||||
((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
|
||||
}else{
|
||||
return SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
if( pOutFlags ){
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
if( isReadWrite ){
|
||||
*pOutFlags = SQLITE_OPEN_READWRITE;
|
||||
}else{
|
||||
*pOutFlags = SQLITE_OPEN_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
pFile->pMethod = &winIoMethod;
|
||||
pFile->h = h;
|
||||
@@ -2142,9 +2230,9 @@ static int winOpen(
|
||||
pFile->pShm = 0;
|
||||
pFile->zPath = zName;
|
||||
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
|
||||
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
|
||||
if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
|
||||
&& !winceCreateLock(zName, pFile)
|
||||
){
|
||||
CloseHandle(h);
|
||||
@@ -2158,8 +2246,9 @@ static int winOpen(
|
||||
{
|
||||
free(zConverted);
|
||||
}
|
||||
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2678,6 +2767,13 @@ int sqlite3_os_init(void){
|
||||
winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/* get memory map allocation granularity */
|
||||
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
|
||||
GetSystemInfo(&winSysInfo);
|
||||
assert(winSysInfo.dwAllocationGranularity > 0);
|
||||
#endif
|
||||
|
||||
sqlite3_vfs_register(&winVfs, 1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+234
-110
@@ -615,7 +615,8 @@ struct Pager {
|
||||
u8 noReadlock; /* Do not bother to obtain readlocks */
|
||||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
|
||||
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
||||
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
u8 memDb; /* True to inhibit all file I/O */
|
||||
@@ -926,7 +927,9 @@ static int assert_pager_state(Pager *p){
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* ifndef NDEBUG */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Return a pointer to a human readable string in a static buffer
|
||||
** containing the state of the Pager object passed as an argument. This
|
||||
@@ -1050,7 +1053,7 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
|
||||
static int pagerUnlockDb(Pager *pPager, int eLock){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( !pPager->exclusiveMode );
|
||||
assert( !pPager->exclusiveMode || pPager->eLock==eLock );
|
||||
assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
|
||||
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
|
||||
if( isOpen(pPager->fd) ){
|
||||
@@ -1297,7 +1300,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
|
||||
rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
|
||||
}
|
||||
if( rc==SQLITE_OK && !pPager->noSync ){
|
||||
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
|
||||
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
|
||||
}
|
||||
|
||||
/* At this point the transaction is committed but the write lock
|
||||
@@ -2474,15 +2477,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
|
||||
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
|
||||
){
|
||||
i64 currentSize, newSize;
|
||||
int szPage = pPager->pageSize;
|
||||
assert( pPager->eLock==EXCLUSIVE_LOCK );
|
||||
/* TODO: Is it safe to use Pager.dbFileSize here? */
|
||||
rc = sqlite3OsFileSize(pPager->fd, ¤tSize);
|
||||
newSize = pPager->pageSize*(i64)nPage;
|
||||
newSize = szPage*(i64)nPage;
|
||||
if( rc==SQLITE_OK && currentSize!=newSize ){
|
||||
if( currentSize>newSize ){
|
||||
rc = sqlite3OsTruncate(pPager->fd, newSize);
|
||||
}else{
|
||||
rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
|
||||
char *pTmp = pPager->pTmpSpace;
|
||||
memset(pTmp, 0, szPage);
|
||||
testcase( (newSize-szPage) < currentSize );
|
||||
testcase( (newSize-szPage) == currentSize );
|
||||
testcase( (newSize-szPage) > currentSize );
|
||||
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->dbFileSize = nPage;
|
||||
@@ -2746,10 +2755,10 @@ end_playback:
|
||||
rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
}
|
||||
if( rc==SQLITE_OK && !pPager->noSync
|
||||
if( rc==SQLITE_OK
|
||||
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
|
||||
){
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
|
||||
rc = sqlite3PagerSync(pPager);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_end_transaction(pPager, zMaster[0]!='\0');
|
||||
@@ -2912,24 +2921,61 @@ static int pagerRollbackWal(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Update the value of the change-counter at offsets 24 and 92 in
|
||||
** the header and the sqlite version number at offset 96.
|
||||
**
|
||||
** This is an unconditional update. See also the pager_incr_changecounter()
|
||||
** routine which only updates the change-counter if the update is actually
|
||||
** needed, as determined by the pPager->changeCountDone state variable.
|
||||
*/
|
||||
static void pager_write_changecounter(PgHdr *pPg){
|
||||
u32 change_counter;
|
||||
|
||||
/* Increment the value just read and write it back to byte 24. */
|
||||
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
|
||||
put32bits(((char*)pPg->pData)+24, change_counter);
|
||||
|
||||
/* Also store the SQLite version number in bytes 96..99 and in
|
||||
** bytes 92..95 store the change counter for which the version number
|
||||
** is valid. */
|
||||
put32bits(((char*)pPg->pData)+92, change_counter);
|
||||
put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a wrapper around sqlite3WalFrames(). As well as logging
|
||||
** the contents of the list of pages headed by pList (connected by pDirty),
|
||||
** this function notifies any active backup processes that the pages have
|
||||
** changed.
|
||||
** changed.
|
||||
**
|
||||
** The list of pages passed into this routine is always sorted by page number.
|
||||
** Hence, if page 1 appears anywhere on the list, it will be the first page.
|
||||
*/
|
||||
static int pagerWalFrames(
|
||||
Pager *pPager, /* Pager object */
|
||||
PgHdr *pList, /* List of frames to log */
|
||||
Pgno nTruncate, /* Database size after this commit */
|
||||
int isCommit, /* True if this is a commit */
|
||||
int sync_flags /* Flags to pass to OsSync() (or 0) */
|
||||
int syncFlags /* Flags to pass to OsSync() (or 0) */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
|
||||
PgHdr *p; /* For looping over pages */
|
||||
#endif
|
||||
|
||||
assert( pPager->pWal );
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Verify that the page list is in accending order */
|
||||
for(p=pList; p && p->pDirty; p=p->pDirty){
|
||||
assert( p->pgno < p->pDirty->pgno );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
rc = sqlite3WalFrames(pPager->pWal,
|
||||
pPager->pageSize, pList, nTruncate, isCommit, sync_flags
|
||||
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
|
||||
);
|
||||
if( rc==SQLITE_OK && pPager->pBackup ){
|
||||
PgHdr *p;
|
||||
@@ -2939,9 +2985,8 @@ static int pagerWalFrames(
|
||||
}
|
||||
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
{
|
||||
PgHdr *p;
|
||||
for(p=pList; p; p=p->pDirty) pager_set_pagehash(p);
|
||||
for(p=pList; p; p=p->pDirty){
|
||||
pager_set_pagehash(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2971,12 +3016,13 @@ static int pagerBeginReadTransaction(Pager *pPager){
|
||||
sqlite3WalEndReadTransaction(pPager->pWal);
|
||||
|
||||
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
|
||||
if( rc==SQLITE_OK && changed ){
|
||||
if( rc!=SQLITE_OK || changed ){
|
||||
pager_reset(pPager);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function is called as part of the transition from PAGER_OPEN
|
||||
@@ -3033,7 +3079,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** Check if the *-wal file that corresponds to the database opened by pPager
|
||||
** exists if the database is not empy, or verify that the *-wal file does
|
||||
@@ -3258,14 +3304,49 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
|
||||
** assurance that the journal will not be corrupted to the
|
||||
** point of causing damage to the database during rollback.
|
||||
**
|
||||
** 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
|
||||
** at the conclusion of the checkpoint if the entire content of the WAL
|
||||
** 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.
|
||||
**
|
||||
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
|
||||
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
|
||||
** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an
|
||||
** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL
|
||||
** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the
|
||||
** synchronous=FULL versus synchronous=NORMAL setting determines when
|
||||
** the xSync primitive is called and is relevant to all platforms.
|
||||
**
|
||||
** Numeric values associated with these states are OFF==1, NORMAL=2,
|
||||
** and FULL=3.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
|
||||
void sqlite3PagerSetSafetyLevel(
|
||||
Pager *pPager, /* The pager to set safety level for */
|
||||
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
|
||||
int bFullFsync, /* PRAGMA fullfsync */
|
||||
int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
|
||||
){
|
||||
assert( level>=1 && level<=3 );
|
||||
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
|
||||
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
|
||||
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
|
||||
if( pPager->noSync ){
|
||||
pPager->syncFlags = 0;
|
||||
pPager->ckptSyncFlags = 0;
|
||||
}else if( bFullFsync ){
|
||||
pPager->syncFlags = SQLITE_SYNC_FULL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
||||
}else if( bCkptFullFsync ){
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
||||
}else{
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3444,9 +3525,8 @@ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
|
||||
if( mxPage>0 ){
|
||||
pPager->mxPgno = mxPage;
|
||||
}
|
||||
if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){
|
||||
pPager->mxPgno = pPager->dbSize;
|
||||
}
|
||||
assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */
|
||||
assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */
|
||||
return pPager->mxPgno;
|
||||
}
|
||||
|
||||
@@ -3651,10 +3731,7 @@ int sqlite3PagerClose(Pager *pPager){
|
||||
/* pPager->errCode = 0; */
|
||||
pPager->exclusiveMode = 0;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
sqlite3WalClose(pPager->pWal,
|
||||
(pPager->noSync ? 0 : pPager->sync_flags),
|
||||
pPager->pageSize, pTmp
|
||||
);
|
||||
sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
|
||||
pPager->pWal = 0;
|
||||
#endif
|
||||
pager_reset(pPager);
|
||||
@@ -3820,7 +3897,7 @@ static int syncJournal(Pager *pPager, int newHdr){
|
||||
if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
|
||||
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
|
||||
IOTRACE(("JSYNC %p\n", pPager))
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
|
||||
@@ -3832,8 +3909,8 @@ static int syncJournal(Pager *pPager, int newHdr){
|
||||
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
|
||||
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
|
||||
IOTRACE(("JSYNC %p\n", pPager))
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
|
||||
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
|
||||
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
@@ -3934,6 +4011,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
char *pData; /* Data to write */
|
||||
|
||||
assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
|
||||
/* Encode the database */
|
||||
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
|
||||
@@ -4226,6 +4304,13 @@ int sqlite3PagerOpen(
|
||||
/* Set the output variable to NULL in case an error occurs. */
|
||||
*ppPager = 0;
|
||||
|
||||
#ifndef SQLITE_OMIT_MEMORYDB
|
||||
if( flags & PAGER_MEMORY ){
|
||||
memDb = 1;
|
||||
zFilename = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Compute and store the full pathname in an allocated buffer pointed
|
||||
** to by zPathname, length nPathname. Or, if this is a temporary file,
|
||||
** leave both nPathname and zPathname set to 0.
|
||||
@@ -4236,17 +4321,8 @@ int sqlite3PagerOpen(
|
||||
if( zPathname==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_MEMORYDB
|
||||
if( strcmp(zFilename,":memory:")==0 ){
|
||||
memDb = 1;
|
||||
zPathname[0] = 0;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
|
||||
}
|
||||
|
||||
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
|
||||
nPathname = sqlite3Strlen30(zPathname);
|
||||
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
|
||||
/* This branch is taken when the journal path required by
|
||||
@@ -4301,19 +4377,15 @@ int sqlite3PagerOpen(
|
||||
|
||||
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
|
||||
if( zPathname ){
|
||||
assert( nPathname>0 );
|
||||
pPager->zJournal = (char*)(pPtr += nPathname + 1);
|
||||
memcpy(pPager->zFilename, zPathname, nPathname);
|
||||
memcpy(pPager->zJournal, zPathname, nPathname);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal", 8);
|
||||
if( pPager->zFilename[0]==0 ){
|
||||
pPager->zJournal[0] = 0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
else{
|
||||
pPager->zWal = &pPager->zJournal[nPathname+8+1];
|
||||
memcpy(pPager->zWal, zPathname, nPathname);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal", 4);
|
||||
}
|
||||
pPager->zWal = &pPager->zJournal[nPathname+8+1];
|
||||
memcpy(pPager->zWal, zPathname, nPathname);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal", 4);
|
||||
#endif
|
||||
sqlite3_free(zPathname);
|
||||
}
|
||||
@@ -4322,9 +4394,10 @@ int sqlite3PagerOpen(
|
||||
|
||||
/* Open the pager file.
|
||||
*/
|
||||
if( zFilename && zFilename[0] && !memDb ){
|
||||
if( zFilename && zFilename[0] ){
|
||||
int fout = 0; /* VFS flags returned by xOpen() */
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
|
||||
assert( !memDb );
|
||||
readOnly = (fout&SQLITE_OPEN_READONLY);
|
||||
|
||||
/* If the file was successfully opened for read/write access,
|
||||
@@ -4428,7 +4501,8 @@ int sqlite3PagerOpen(
|
||||
assert( useJournal || pPager->tempFile );
|
||||
pPager->noSync = pPager->tempFile;
|
||||
pPager->fullSync = pPager->noSync ?0:1;
|
||||
pPager->sync_flags = SQLITE_SYNC_NORMAL;
|
||||
pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = pPager->syncFlags;
|
||||
/* pPager->pFirst = 0; */
|
||||
/* pPager->pFirstSynced = 0; */
|
||||
/* pPager->pLast = 0; */
|
||||
@@ -4528,7 +4602,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
|
||||
sqlite3BeginBenignMalloc();
|
||||
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
|
||||
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
pagerUnlockDb(pPager, SHARED_LOCK);
|
||||
if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
}else{
|
||||
@@ -4778,7 +4852,9 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
** mode. Otherwise, the following function call is a no-op.
|
||||
*/
|
||||
rc = pagerOpenWalIfPresent(pPager);
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
assert( pPager->pWal==0 || rc==SQLITE_OK );
|
||||
#endif
|
||||
}
|
||||
|
||||
if( pagerUseWal(pPager) ){
|
||||
@@ -5207,29 +5283,29 @@ static int pager_write(PgHdr *pPg){
|
||||
|
||||
CHECK_PAGE(pPg);
|
||||
|
||||
/* The journal file needs to be opened. Higher level routines have already
|
||||
** obtained the necessary locks to begin the write-transaction, but the
|
||||
** rollback journal might not yet be open. Open it now if this is the case.
|
||||
**
|
||||
** This is done before calling sqlite3PcacheMakeDirty() on the page.
|
||||
** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then
|
||||
** an error might occur and the pager would end up in WRITER_LOCKED state
|
||||
** with pages marked as dirty in the cache.
|
||||
*/
|
||||
if( pPager->eState==PAGER_WRITER_LOCKED ){
|
||||
rc = pager_open_journal(pPager);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
|
||||
assert( assert_pager_state(pPager) );
|
||||
|
||||
/* Mark the page as dirty. If the page has already been written
|
||||
** to the journal then we can return right away.
|
||||
*/
|
||||
sqlite3PcacheMakeDirty(pPg);
|
||||
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
|
||||
assert( !pagerUseWal(pPager) );
|
||||
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
|
||||
}else{
|
||||
|
||||
/* If we get this far, it means that the page needs to be
|
||||
** written to the transaction journal or the checkpoint journal
|
||||
** or both.
|
||||
**
|
||||
** Higher level routines have already obtained the necessary locks
|
||||
** to begin the write-transaction, but the rollback journal might not
|
||||
** yet be open. Open it now if this is the case.
|
||||
*/
|
||||
if( pPager->eState==PAGER_WRITER_LOCKED ){
|
||||
rc = pager_open_journal(pPager);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
|
||||
assert( assert_pager_state(pPager) );
|
||||
|
||||
/* The transaction journal now exists and we have a RESERVED or an
|
||||
** EXCLUSIVE lock on the main database file. Write the current page to
|
||||
@@ -5456,7 +5532,13 @@ void sqlite3PagerDontWrite(PgHdr *pPg){
|
||||
/*
|
||||
** This routine is called to increment the value of the database file
|
||||
** change-counter, stored as a 4-byte big-endian integer starting at
|
||||
** byte offset 24 of the pager file.
|
||||
** byte offset 24 of the pager file. The secondary change counter at
|
||||
** 92 is also updated, as is the SQLite version number at offset 96.
|
||||
**
|
||||
** But this only happens if the pPager->changeCountDone flag is false.
|
||||
** To avoid excess churning of page 1, the update only happens once.
|
||||
** See also the pager_write_changecounter() routine that does an
|
||||
** unconditional update of the change counters.
|
||||
**
|
||||
** If the isDirectMode flag is zero, then this is done by calling
|
||||
** sqlite3PagerWrite() on page 1, then modifying the contents of the
|
||||
@@ -5497,7 +5579,6 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
|
||||
if( !pPager->changeCountDone && pPager->dbSize>0 ){
|
||||
PgHdr *pPgHdr; /* Reference to page 1 */
|
||||
u32 change_counter; /* Initial value of change-counter field */
|
||||
|
||||
assert( !pPager->tempFile && isOpen(pPager->fd) );
|
||||
|
||||
@@ -5515,16 +5596,8 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* Increment the value just read and write it back to byte 24. */
|
||||
change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers);
|
||||
change_counter++;
|
||||
put32bits(((char*)pPgHdr->pData)+24, change_counter);
|
||||
|
||||
/* Also store the SQLite version number in bytes 96..99 and in
|
||||
** bytes 92..95 store the change counter for which the version number
|
||||
** is valid. */
|
||||
put32bits(((char*)pPgHdr->pData)+92, change_counter);
|
||||
put32bits(((char*)pPgHdr->pData)+96, SQLITE_VERSION_NUMBER);
|
||||
/* Actually do the update of the change counter */
|
||||
pager_write_changecounter(pPgHdr);
|
||||
|
||||
/* If running in direct mode, write the contents of page 1 to the file. */
|
||||
if( DIRECT_MODE ){
|
||||
@@ -5549,19 +5622,20 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the pager file to disk. This is a no-op for in-memory files
|
||||
** Sync the database file to disk. This is a no-op for in-memory databases
|
||||
** or pages with the Pager.noSync flag set.
|
||||
**
|
||||
** If successful, or called on a pager for which it is a no-op, this
|
||||
** If successful, or if called on a pager for which it is a no-op, this
|
||||
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
|
||||
*/
|
||||
int sqlite3PagerSync(Pager *pPager){
|
||||
int rc; /* Return code */
|
||||
assert( !MEMDB );
|
||||
if( pPager->noSync ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
|
||||
int rc = SQLITE_OK;
|
||||
if( !pPager->noSync ){
|
||||
assert( !MEMDB );
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
|
||||
}else if( isOpen(pPager->fd) ){
|
||||
assert( !MEMDB );
|
||||
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -5650,7 +5724,7 @@ int sqlite3PagerCommitPhaseOne(
|
||||
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
||||
if( pList ){
|
||||
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
|
||||
(pPager->fullSync ? pPager->sync_flags : 0)
|
||||
(pPager->fullSync ? pPager->syncFlags : 0)
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -5780,8 +5854,8 @@ int sqlite3PagerCommitPhaseOne(
|
||||
}
|
||||
|
||||
/* Finally, sync the database file. */
|
||||
if( !pPager->noSync && !noSync ){
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
|
||||
if( !noSync ){
|
||||
rc = sqlite3PagerSync(pPager);
|
||||
}
|
||||
IOTRACE(("DBSYNC %p\n", pPager))
|
||||
}
|
||||
@@ -5893,7 +5967,17 @@ int sqlite3PagerRollback(Pager *pPager){
|
||||
rc2 = pager_end_transaction(pPager, pPager->setMaster);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
|
||||
int eState = pPager->eState;
|
||||
rc = pager_end_transaction(pPager, 0);
|
||||
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
|
||||
/* This can happen using journal_mode=off. Move the pager to the error
|
||||
** state to indicate that the contents of the cache may not be trusted.
|
||||
** Any active readers will get SQLITE_ABORT.
|
||||
*/
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
rc = pager_playback(pPager, 0);
|
||||
}
|
||||
@@ -6352,7 +6436,8 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
|
||||
|| eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
|
||||
assert( PAGER_LOCKINGMODE_QUERY<0 );
|
||||
assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 );
|
||||
if( eMode>=0 && !pPager->tempFile ){
|
||||
assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) );
|
||||
if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){
|
||||
pPager->exclusiveMode = (u8)eMode;
|
||||
}
|
||||
return (int)pPager->exclusiveMode;
|
||||
@@ -6521,10 +6606,8 @@ int sqlite3PagerCheckpoint(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
if( pPager->pWal ){
|
||||
u8 *zBuf = (u8 *)pPager->pTmpSpace;
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal,
|
||||
(pPager->noSync ? 0 : pPager->sync_flags),
|
||||
pPager->pageSize, zBuf
|
||||
);
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags,
|
||||
pPager->pageSize, zBuf);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -6539,9 +6622,61 @@ int sqlite3PagerWalCallback(Pager *pPager){
|
||||
*/
|
||||
int sqlite3PagerWalSupported(Pager *pPager){
|
||||
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
|
||||
return pMethods->iVersion>=2 && pMethods->xShmMap!=0;
|
||||
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to take an exclusive lock on the database file. If a PENDING lock
|
||||
** is obtained instead, immediately release it.
|
||||
*/
|
||||
static int pagerExclusiveLock(Pager *pPager){
|
||||
int rc; /* Return code */
|
||||
|
||||
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
|
||||
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* If the attempt to grab the pending lock failed, release the
|
||||
** exclusive lock that may have been obtained instead. */
|
||||
pagerUnlockDb(pPager, SHARED_LOCK);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Call sqlite3WalOpen() to open the WAL handle. If the pager is in
|
||||
** exclusive-locking mode when this function is called, take an EXCLUSIVE
|
||||
** lock on the database file and use heap-memory to store the wal-index
|
||||
** in. Otherwise, use the normal shared-memory.
|
||||
*/
|
||||
static int pagerOpenWal(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( pPager->pWal==0 && pPager->tempFile==0 );
|
||||
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock);
|
||||
|
||||
/* If the pager is already in exclusive-mode, the WAL module will use
|
||||
** heap-memory for the wal-index instead of the VFS shared-memory
|
||||
** implementation. Take the exclusive lock now, before opening the WAL
|
||||
** file, to make sure this is safe.
|
||||
*/
|
||||
if( pPager->exclusiveMode ){
|
||||
rc = pagerExclusiveLock(pPager);
|
||||
}
|
||||
|
||||
/* Open the connection to the log file. If this operation fails,
|
||||
** (e.g. due to malloc() failure), return an error code.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3WalOpen(pPager->pVfs,
|
||||
pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The caller must be holding a SHARED lock on the database file to call
|
||||
** this function.
|
||||
@@ -6575,11 +6710,7 @@ int sqlite3PagerOpenWal(
|
||||
/* Close any rollback journal previously open */
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
|
||||
/* Open the connection to the log file. If this operation fails,
|
||||
** (e.g. due to malloc() failure), unlock the database file and
|
||||
** return an error code.
|
||||
*/
|
||||
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
|
||||
rc = pagerOpenWal(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->journalMode = PAGER_JOURNALMODE_WAL;
|
||||
pPager->eState = PAGER_OPEN;
|
||||
@@ -6618,8 +6749,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && logexists ){
|
||||
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
|
||||
pPager->zWal, &pPager->pWal);
|
||||
rc = pagerOpenWal(pPager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6627,17 +6757,11 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
** the database file, the log and log-summary files will be deleted.
|
||||
*/
|
||||
if( rc==SQLITE_OK && pPager->pWal ){
|
||||
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
|
||||
rc = pagerExclusiveLock(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3WalClose(pPager->pWal,
|
||||
(pPager->noSync ? 0 : pPager->sync_flags),
|
||||
pPager->pageSize, (u8*)pPager->pTmpSpace
|
||||
);
|
||||
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
|
||||
pPager->pageSize, (u8*)pPager->pTmpSpace);
|
||||
pPager->pWal = 0;
|
||||
}else{
|
||||
/* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
|
||||
** that we did get back to SHARED. */
|
||||
pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
||||
+2
-1
@@ -59,6 +59,7 @@ typedef struct PgHdr DbPage;
|
||||
*/
|
||||
#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
|
||||
#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
|
||||
#define PAGER_MEMORY 0x0004 /* In-memory database */
|
||||
|
||||
/*
|
||||
** Valid values for the second argument to sqlite3PagerLockingMode().
|
||||
@@ -102,7 +103,7 @@ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
|
||||
int sqlite3PagerSetPagesize(Pager*, u32*, int);
|
||||
int sqlite3PagerMaxPageCount(Pager*, int);
|
||||
void sqlite3PagerSetCachesize(Pager*, int);
|
||||
void sqlite3PagerSetSafetyLevel(Pager*,int,int);
|
||||
void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
|
||||
int sqlite3PagerLockingMode(Pager *, int);
|
||||
int sqlite3PagerSetJournalMode(Pager *, int);
|
||||
int sqlite3PagerGetJournalMode(Pager*);
|
||||
|
||||
@@ -142,12 +142,16 @@ static void pcacheUnpin(PgHdr *p){
|
||||
*/
|
||||
int sqlite3PcacheInitialize(void){
|
||||
if( sqlite3GlobalConfig.pcache.xInit==0 ){
|
||||
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
|
||||
** built-in default page cache is used instead of the application defined
|
||||
** page cache. */
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
|
||||
}
|
||||
void sqlite3PcacheShutdown(void){
|
||||
if( sqlite3GlobalConfig.pcache.xShutdown ){
|
||||
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
|
||||
sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
|
||||
}
|
||||
}
|
||||
|
||||
+298
-135
@@ -22,24 +22,62 @@
|
||||
typedef struct PCache1 PCache1;
|
||||
typedef struct PgHdr1 PgHdr1;
|
||||
typedef struct PgFreeslot PgFreeslot;
|
||||
typedef struct PGroup PGroup;
|
||||
|
||||
/* Pointers to structures of this type are cast and returned as
|
||||
** opaque sqlite3_pcache* handles
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each others unpinned
|
||||
** pages when they are under memory pressure. A PGroup is an instance of
|
||||
** the following object.
|
||||
**
|
||||
** This page cache implementation works in one of two modes:
|
||||
**
|
||||
** (1) Every PCache is the sole member of its own PGroup. There is
|
||||
** one PGroup per PCache.
|
||||
**
|
||||
** (2) There is a single global PGroup that all PCaches are a member
|
||||
** of.
|
||||
**
|
||||
** Mode 1 uses more memory (since PCache instances are not able to rob
|
||||
** unused pages from other PCaches) but it also operates without a mutex,
|
||||
** and is therefore often faster. Mode 2 requires a mutex in order to be
|
||||
** threadsafe, but is able recycle pages more efficient.
|
||||
**
|
||||
** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
|
||||
** PGroup which is the pcache1.grp global variable and its mutex is
|
||||
** SQLITE_MUTEX_STATIC_LRU.
|
||||
*/
|
||||
struct PGroup {
|
||||
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
|
||||
int nMaxPage; /* Sum of nMax for purgeable caches */
|
||||
int nMinPage; /* Sum of nMin for purgeable caches */
|
||||
int mxPinned; /* nMaxpage + 10 - nMinPage */
|
||||
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||
};
|
||||
|
||||
/* Each page cache is an instance of the following object. Every
|
||||
** open database file (including each in-memory database and each
|
||||
** temporary or transient database) has a single page cache which
|
||||
** is an instance of this object.
|
||||
**
|
||||
** Pointers to structures of this type are cast and returned as
|
||||
** opaque sqlite3_pcache* handles.
|
||||
*/
|
||||
struct PCache1 {
|
||||
/* Cache configuration parameters. Page size (szPage) and the purgeable
|
||||
** flag (bPurgeable) are set when the cache is created. nMax may be
|
||||
** modified at any time by a call to the pcache1CacheSize() method.
|
||||
** The global mutex must be held when accessing nMax.
|
||||
** The PGroup mutex must be held when accessing nMax.
|
||||
*/
|
||||
PGroup *pGroup; /* PGroup this cache belongs to */
|
||||
int szPage; /* Size of allocated pages in bytes */
|
||||
int bPurgeable; /* True if cache is purgeable */
|
||||
unsigned int nMin; /* Minimum number of pages reserved */
|
||||
unsigned int nMax; /* Configured "cache_size" value */
|
||||
unsigned int n90pct; /* nMax*9/10 */
|
||||
|
||||
/* Hash table of all pages. The following variables may only be accessed
|
||||
** when the accessor is holding the global mutex (see pcache1EnterMutex()
|
||||
** and pcache1LeaveMutex()).
|
||||
** when the accessor is holding the PGroup mutex.
|
||||
*/
|
||||
unsigned int nRecyclable; /* Number of pages in the LRU list */
|
||||
unsigned int nPage; /* Total number of pages in apHash */
|
||||
@@ -75,18 +113,27 @@ struct PgFreeslot {
|
||||
** Global data used by this cache.
|
||||
*/
|
||||
static SQLITE_WSD struct PCacheGlobal {
|
||||
sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */
|
||||
PGroup grp; /* The global PGroup for mode (2) */
|
||||
|
||||
int nMaxPage; /* Sum of nMaxPage for purgeable caches */
|
||||
int nMinPage; /* Sum of nMinPage for purgeable caches */
|
||||
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||
|
||||
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
|
||||
int szSlot; /* Size of each free slot */
|
||||
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
|
||||
PgFreeslot *pFree; /* Free page blocks */
|
||||
int isInit; /* True if initialized */
|
||||
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. The
|
||||
** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
|
||||
** fixed at sqlite3_initialize() time and do not require mutex protection.
|
||||
** The nFreeSlot and pFree values do require mutex protection.
|
||||
*/
|
||||
int isInit; /* True if initialized */
|
||||
int szSlot; /* Size of each free slot */
|
||||
int nSlot; /* The number of pcache slots */
|
||||
int nReserve; /* Try to keep nFreeSlot above this */
|
||||
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
|
||||
/* Above requires no mutex. Use mutex below for variable that follow. */
|
||||
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
|
||||
int nFreeSlot; /* Number of unused pcache slots */
|
||||
PgFreeslot *pFree; /* Free page blocks */
|
||||
/* The following value requires a mutex to change. We skip the mutex on
|
||||
** reading because (1) most platforms read a 32-bit integer atomically and
|
||||
** (2) even if an incorrect value is read, no great harm is done since this
|
||||
** is really just an optimization. */
|
||||
int bUnderPressure; /* True if low on PAGECACHE memory */
|
||||
} pcache1_g;
|
||||
|
||||
/*
|
||||
@@ -112,10 +159,10 @@ static SQLITE_WSD struct PCacheGlobal {
|
||||
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
|
||||
|
||||
/*
|
||||
** Macros to enter and leave the global LRU mutex.
|
||||
** Macros to enter and leave the PCache LRU mutex.
|
||||
*/
|
||||
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
|
||||
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)
|
||||
#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
|
||||
#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
|
||||
|
||||
/******************************************************************************/
|
||||
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
|
||||
@@ -125,14 +172,20 @@ static SQLITE_WSD struct PCacheGlobal {
|
||||
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
|
||||
** verb to sqlite3_config(). Parameter pBuf points to an allocation large
|
||||
** enough to contain 'n' buffers of 'sz' bytes each.
|
||||
**
|
||||
** This routine is called from sqlite3_initialize() and so it is guaranteed
|
||||
** to be serialized already. There is no need for further mutexing.
|
||||
*/
|
||||
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
if( pcache1.isInit ){
|
||||
PgFreeslot *p;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pcache1.szSlot = sz;
|
||||
pcache1.nSlot = pcache1.nFreeSlot = n;
|
||||
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
|
||||
pcache1.pStart = pBuf;
|
||||
pcache1.pFree = 0;
|
||||
pcache1.bUnderPressure = 0;
|
||||
while( n-- ){
|
||||
p = (PgFreeslot*)pBuf;
|
||||
p->pNext = pcache1.pFree;
|
||||
@@ -148,30 +201,36 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
|
||||
** such buffer exists or there is no space left in it, this function falls
|
||||
** back to sqlite3Malloc().
|
||||
**
|
||||
** Multiple threads can run this routine at the same time. Global variables
|
||||
** in pcache1 need to be protected via mutex.
|
||||
*/
|
||||
static void *pcache1Alloc(int nByte){
|
||||
void *p;
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
void *p = 0;
|
||||
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
|
||||
if( nByte<=pcache1.szSlot && pcache1.pFree ){
|
||||
assert( pcache1.isInit );
|
||||
if( nByte<=pcache1.szSlot ){
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
p = (PgHdr1 *)pcache1.pFree;
|
||||
pcache1.pFree = pcache1.pFree->pNext;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||
}else{
|
||||
|
||||
/* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
|
||||
** global pcache mutex and unlock the pager-cache object pCache. This is
|
||||
** so that if the attempt to allocate a new buffer causes the the
|
||||
** configured soft-heap-limit to be breached, it will be possible to
|
||||
** reclaim memory from this pager-cache.
|
||||
if( p ){
|
||||
pcache1.pFree = pcache1.pFree->pNext;
|
||||
pcache1.nFreeSlot--;
|
||||
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
|
||||
assert( pcache1.nFreeSlot>=0 );
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||
}
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}
|
||||
if( p==0 ){
|
||||
/* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get
|
||||
** it from sqlite3Malloc instead.
|
||||
*/
|
||||
pcache1LeaveMutex();
|
||||
p = sqlite3Malloc(nByte);
|
||||
pcache1EnterMutex();
|
||||
if( p ){
|
||||
int sz = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
|
||||
}
|
||||
@@ -182,30 +241,35 @@ static void *pcache1Alloc(int nByte){
|
||||
** Free an allocated buffer obtained from pcache1Alloc().
|
||||
*/
|
||||
static void pcache1Free(void *p){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( p==0 ) return;
|
||||
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
||||
PgFreeslot *pSlot;
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
|
||||
pSlot = (PgFreeslot*)p;
|
||||
pSlot->pNext = pcache1.pFree;
|
||||
pcache1.pFree = pSlot;
|
||||
pcache1.nFreeSlot++;
|
||||
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
|
||||
assert( pcache1.nFreeSlot<=pcache1.nSlot );
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}else{
|
||||
int iSize;
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
iSize = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/*
|
||||
** Return the size of a pache allocation
|
||||
** Return the size of a pcache allocation
|
||||
*/
|
||||
static int pcache1MemSize(void *p){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
||||
return pcache1.szSlot;
|
||||
}else{
|
||||
@@ -229,7 +293,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
if( pPg ){
|
||||
p = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
if( pCache->bPurgeable ){
|
||||
pcache1.nCurrentPage++;
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
}else{
|
||||
p = 0;
|
||||
@@ -246,8 +310,9 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
*/
|
||||
static void pcache1FreePage(PgHdr1 *p){
|
||||
if( ALWAYS(p) ){
|
||||
if( p->pCache->bPurgeable ){
|
||||
pcache1.nCurrentPage--;
|
||||
PCache1 *pCache = p->pCache;
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage--;
|
||||
}
|
||||
pcache1Free(PGHDR1_TO_PAGE(p));
|
||||
}
|
||||
@@ -259,20 +324,39 @@ static void pcache1FreePage(PgHdr1 *p){
|
||||
** exists, this function falls back to sqlite3Malloc().
|
||||
*/
|
||||
void *sqlite3PageMalloc(int sz){
|
||||
void *p;
|
||||
pcache1EnterMutex();
|
||||
p = pcache1Alloc(sz);
|
||||
pcache1LeaveMutex();
|
||||
return p;
|
||||
return pcache1Alloc(sz);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free an allocated buffer obtained from sqlite3PageMalloc().
|
||||
*/
|
||||
void sqlite3PageFree(void *p){
|
||||
pcache1EnterMutex();
|
||||
pcache1Free(p);
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return true if it desirable to avoid allocating a new page cache
|
||||
** entry.
|
||||
**
|
||||
** If memory was allocated specifically to the page cache using
|
||||
** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then
|
||||
** it is desirable to avoid allocating a new page cache entry because
|
||||
** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient
|
||||
** for all page cache needs and we should not need to spill the
|
||||
** allocation onto the heap.
|
||||
**
|
||||
** Or, the heap is used for all page cache memory put the heap is
|
||||
** under memory pressure, then again it is desirable to avoid
|
||||
** allocating a new page cache entry in order to avoid stressing
|
||||
** the heap even further.
|
||||
*/
|
||||
static int pcache1UnderMemoryPressure(PCache1 *pCache){
|
||||
if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
|
||||
return pcache1.bUnderPressure;
|
||||
}else{
|
||||
return sqlite3HeapNearlyFull();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -282,25 +366,25 @@ void sqlite3PageFree(void *p){
|
||||
** This function is used to resize the hash table used by the cache passed
|
||||
** as the first argument.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
** The PCache mutex must be held when this function is called.
|
||||
*/
|
||||
static int pcache1ResizeHash(PCache1 *p){
|
||||
PgHdr1 **apNew;
|
||||
unsigned int nNew;
|
||||
unsigned int i;
|
||||
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
assert( sqlite3_mutex_held(p->pGroup->mutex) );
|
||||
|
||||
nNew = p->nHash*2;
|
||||
if( nNew<256 ){
|
||||
nNew = 256;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(p->pGroup);
|
||||
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
|
||||
apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
|
||||
if( p->nHash ){ sqlite3EndBenignMalloc(); }
|
||||
pcache1EnterMutex();
|
||||
pcache1EnterMutex(p->pGroup);
|
||||
if( apNew ){
|
||||
memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
|
||||
for(i=0; i<p->nHash; i++){
|
||||
@@ -323,25 +407,33 @@ static int pcache1ResizeHash(PCache1 *p){
|
||||
|
||||
/*
|
||||
** This function is used internally to remove the page pPage from the
|
||||
** global LRU list, if is part of it. If pPage is not part of the global
|
||||
** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
|
||||
** LRU list, then this function is a no-op.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
** The PGroup mutex must be held when this function is called.
|
||||
**
|
||||
** If pPage is NULL then this routine is a no-op.
|
||||
*/
|
||||
static void pcache1PinPage(PgHdr1 *pPage){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){
|
||||
PCache1 *pCache;
|
||||
PGroup *pGroup;
|
||||
|
||||
if( pPage==0 ) return;
|
||||
pCache = pPage->pCache;
|
||||
pGroup = pCache->pGroup;
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
if( pPage->pLruNext || pPage==pGroup->pLruTail ){
|
||||
if( pPage->pLruPrev ){
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
}
|
||||
if( pPage->pLruNext ){
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
}
|
||||
if( pcache1.pLruHead==pPage ){
|
||||
pcache1.pLruHead = pPage->pLruNext;
|
||||
if( pGroup->pLruHead==pPage ){
|
||||
pGroup->pLruHead = pPage->pLruNext;
|
||||
}
|
||||
if( pcache1.pLruTail==pPage ){
|
||||
pcache1.pLruTail = pPage->pLruPrev;
|
||||
if( pGroup->pLruTail==pPage ){
|
||||
pGroup->pLruTail = pPage->pLruPrev;
|
||||
}
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
@@ -354,13 +446,14 @@ static void pcache1PinPage(PgHdr1 *pPage){
|
||||
** Remove the page supplied as an argument from the hash table
|
||||
** (PCache1.apHash structure) that it is currently stored in.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
** The PGroup mutex must be held when this function is called.
|
||||
*/
|
||||
static void pcache1RemoveFromHash(PgHdr1 *pPage){
|
||||
unsigned int h;
|
||||
PCache1 *pCache = pPage->pCache;
|
||||
PgHdr1 **pp;
|
||||
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
h = pPage->iKey % pCache->nHash;
|
||||
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
|
||||
*pp = (*pp)->pNext;
|
||||
@@ -369,13 +462,14 @@ static void pcache1RemoveFromHash(PgHdr1 *pPage){
|
||||
}
|
||||
|
||||
/*
|
||||
** If there are currently more than pcache.nMaxPage pages allocated, try
|
||||
** to recycle pages to reduce the number allocated to pcache.nMaxPage.
|
||||
** If there are currently more than nMaxPage pages allocated, try
|
||||
** to recycle pages to reduce the number allocated to nMaxPage.
|
||||
*/
|
||||
static void pcache1EnforceMaxPage(void){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){
|
||||
PgHdr1 *p = pcache1.pLruTail;
|
||||
static void pcache1EnforceMaxPage(PGroup *pGroup){
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
|
||||
PgHdr1 *p = pGroup->pLruTail;
|
||||
assert( p->pCache->pGroup==pGroup );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
@@ -387,15 +481,15 @@ static void pcache1EnforceMaxPage(void){
|
||||
** greater than or equal to iLimit. Any pinned pages that meet this
|
||||
** criteria are unpinned before they are discarded.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
** The PCache mutex must be held when this function is called.
|
||||
*/
|
||||
static void pcache1TruncateUnsafe(
|
||||
PCache1 *pCache,
|
||||
unsigned int iLimit
|
||||
PCache1 *pCache, /* The cache to truncate */
|
||||
unsigned int iLimit /* Drop pages with this pgno or larger */
|
||||
){
|
||||
TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */
|
||||
TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */
|
||||
unsigned int h;
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
for(h=0; h<pCache->nHash; h++){
|
||||
PgHdr1 **pp = &pCache->apHash[h];
|
||||
PgHdr1 *pPage;
|
||||
@@ -425,8 +519,10 @@ static int pcache1Init(void *NotUsed){
|
||||
assert( pcache1.isInit==0 );
|
||||
memset(&pcache1, 0, sizeof(pcache1));
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
|
||||
pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
|
||||
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
|
||||
}
|
||||
pcache1.grp.mxPinned = 10;
|
||||
pcache1.isInit = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -448,18 +544,47 @@ static void pcache1Shutdown(void *NotUsed){
|
||||
** Allocate a new cache.
|
||||
*/
|
||||
static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||
PCache1 *pCache;
|
||||
PCache1 *pCache; /* The newly created page cache */
|
||||
PGroup *pGroup; /* The group the new page cache will belong to */
|
||||
int sz; /* Bytes of memory required to allocate the new cache */
|
||||
|
||||
pCache = (PCache1 *)sqlite3_malloc(sizeof(PCache1));
|
||||
/*
|
||||
** The seperateCache variable is true if each PCache has its own private
|
||||
** PGroup. In other words, separateCache is true for mode (1) where no
|
||||
** mutexing is required.
|
||||
**
|
||||
** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
|
||||
**
|
||||
** * Always use a unified cache in single-threaded applications
|
||||
**
|
||||
** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
|
||||
** use separate caches (mode-1)
|
||||
*/
|
||||
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
|
||||
const int separateCache = 0;
|
||||
#else
|
||||
int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
|
||||
#endif
|
||||
|
||||
sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
|
||||
pCache = (PCache1 *)sqlite3_malloc(sz);
|
||||
if( pCache ){
|
||||
memset(pCache, 0, sizeof(PCache1));
|
||||
memset(pCache, 0, sz);
|
||||
if( separateCache ){
|
||||
pGroup = (PGroup*)&pCache[1];
|
||||
pGroup->mxPinned = 10;
|
||||
}else{
|
||||
pGroup = &pcache1_g.grp;
|
||||
}
|
||||
pCache->pGroup = pGroup;
|
||||
pCache->szPage = szPage;
|
||||
pCache->bPurgeable = (bPurgeable ? 1 : 0);
|
||||
if( bPurgeable ){
|
||||
pCache->nMin = 10;
|
||||
pcache1EnterMutex();
|
||||
pcache1.nMinPage += pCache->nMin;
|
||||
pcache1LeaveMutex();
|
||||
pcache1EnterMutex(pGroup);
|
||||
pGroup->nMinPage += pCache->nMin;
|
||||
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
||||
pcache1LeaveMutex(pGroup);
|
||||
}
|
||||
}
|
||||
return (sqlite3_pcache *)pCache;
|
||||
@@ -473,11 +598,14 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
if( pCache->bPurgeable ){
|
||||
pcache1EnterMutex();
|
||||
pcache1.nMaxPage += (nMax - pCache->nMax);
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
pcache1EnterMutex(pGroup);
|
||||
pGroup->nMaxPage += (nMax - pCache->nMax);
|
||||
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
||||
pCache->nMax = nMax;
|
||||
pcache1EnforceMaxPage();
|
||||
pcache1LeaveMutex();
|
||||
pCache->n90pct = pCache->nMax*9/10;
|
||||
pcache1EnforceMaxPage(pGroup);
|
||||
pcache1LeaveMutex(pGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,9 +614,10 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
|
||||
*/
|
||||
static int pcache1Pagecount(sqlite3_pcache *p){
|
||||
int n;
|
||||
pcache1EnterMutex();
|
||||
n = ((PCache1 *)p)->nPage;
|
||||
pcache1LeaveMutex();
|
||||
PCache1 *pCache = (PCache1*)p;
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
n = pCache->nPage;
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -516,14 +645,16 @@ static int pcache1Pagecount(sqlite3_pcache *p){
|
||||
** 2. If createFlag==0 and the page is not already in the cache, NULL is
|
||||
** returned.
|
||||
**
|
||||
** 3. If createFlag is 1, and the page is not already in the cache,
|
||||
** and if either of the following are true, return NULL:
|
||||
** 3. If createFlag is 1, and the page is not already in the cache, then
|
||||
** return NULL (do not allocate a new page) if any of the following
|
||||
** conditions are true:
|
||||
**
|
||||
** (a) the number of pages pinned by the cache is greater than
|
||||
** PCache1.nMax, or
|
||||
**
|
||||
** (b) the number of pages pinned by the cache is greater than
|
||||
** the sum of nMax for all purgeable caches, less the sum of
|
||||
** nMin for all other purgeable caches.
|
||||
** nMin for all other purgeable caches, or
|
||||
**
|
||||
** 4. If none of the first three conditions apply and the cache is marked
|
||||
** as purgeable, and if one of the following is true:
|
||||
@@ -535,6 +666,9 @@ static int pcache1Pagecount(sqlite3_pcache *p){
|
||||
** already equal to or greater than the sum of nMax for all
|
||||
** purgeable caches,
|
||||
**
|
||||
** (c) The system is under memory pressure and wants to avoid
|
||||
** unnecessary pages cache entry allocations
|
||||
**
|
||||
** then attempt to recycle a page from the LRU list. If it is the right
|
||||
** size, return the recycled buffer. Otherwise, free the buffer and
|
||||
** proceed to step 5.
|
||||
@@ -542,30 +676,50 @@ static int pcache1Pagecount(sqlite3_pcache *p){
|
||||
** 5. Otherwise, allocate and return a new page buffer.
|
||||
*/
|
||||
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
unsigned int nPinned;
|
||||
int nPinned;
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PGroup *pGroup;
|
||||
PgHdr1 *pPage = 0;
|
||||
|
||||
assert( pCache->bPurgeable || createFlag!=1 );
|
||||
pcache1EnterMutex();
|
||||
if( createFlag==1 ) sqlite3BeginBenignMalloc();
|
||||
assert( pCache->bPurgeable || pCache->nMin==0 );
|
||||
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
|
||||
assert( pCache->nMin==0 || pCache->bPurgeable );
|
||||
pcache1EnterMutex(pGroup = pCache->pGroup);
|
||||
|
||||
/* Search the hash table for an existing entry. */
|
||||
/* Step 1: Search the hash table for an existing entry. */
|
||||
if( pCache->nHash>0 ){
|
||||
unsigned int h = iKey % pCache->nHash;
|
||||
for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
|
||||
}
|
||||
|
||||
/* Step 2: Abort if no existing page is found and createFlag is 0 */
|
||||
if( pPage || createFlag==0 ){
|
||||
pcache1PinPage(pPage);
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
/* Step 3 of header comment. */
|
||||
/* The pGroup local variable will normally be initialized by the
|
||||
** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
|
||||
** then pcache1EnterMutex() is a no-op, so we have to initialize the
|
||||
** local variable here. Delaying the initialization of pGroup is an
|
||||
** optimization: The common case is to exit the module before reaching
|
||||
** this point.
|
||||
*/
|
||||
#ifdef SQLITE_MUTEX_OMIT
|
||||
pGroup = pCache->pGroup;
|
||||
#endif
|
||||
|
||||
|
||||
/* Step 3: Abort if createFlag is 1 but the cache is nearly full */
|
||||
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||
assert( nPinned>=0 );
|
||||
assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
|
||||
assert( pCache->n90pct == pCache->nMax*9/10 );
|
||||
if( createFlag==1 && (
|
||||
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|
||||
|| nPinned>=(pCache->nMax * 9 / 10)
|
||||
nPinned>=pGroup->mxPinned
|
||||
|| nPinned>=(int)pCache->n90pct
|
||||
|| pcache1UnderMemoryPressure(pCache)
|
||||
)){
|
||||
goto fetch_out;
|
||||
}
|
||||
@@ -574,18 +728,22 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
/* Step 4. Try to recycle a page buffer if appropriate. */
|
||||
if( pCache->bPurgeable && pcache1.pLruTail && (
|
||||
(pCache->nPage+1>=pCache->nMax) || pcache1.nCurrentPage>=pcache1.nMaxPage
|
||||
/* Step 4. Try to recycle a page. */
|
||||
if( pCache->bPurgeable && pGroup->pLruTail && (
|
||||
(pCache->nPage+1>=pCache->nMax)
|
||||
|| pGroup->nCurrentPage>=pGroup->nMaxPage
|
||||
|| pcache1UnderMemoryPressure(pCache)
|
||||
)){
|
||||
pPage = pcache1.pLruTail;
|
||||
PCache1 *pOtherCache;
|
||||
pPage = pGroup->pLruTail;
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1PinPage(pPage);
|
||||
if( pPage->pCache->szPage!=pCache->szPage ){
|
||||
if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
|
||||
pcache1FreePage(pPage);
|
||||
pPage = 0;
|
||||
}else{
|
||||
pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable);
|
||||
pGroup->nCurrentPage -=
|
||||
(pOtherCache->bPurgeable - pCache->bPurgeable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +751,11 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
** attempt to allocate a new one.
|
||||
*/
|
||||
if( !pPage ){
|
||||
if( createFlag==1 ) sqlite3BeginBenignMalloc();
|
||||
pcache1LeaveMutex(pGroup);
|
||||
pPage = pcache1AllocPage(pCache);
|
||||
pcache1EnterMutex(pGroup);
|
||||
if( createFlag==1 ) sqlite3EndBenignMalloc();
|
||||
}
|
||||
|
||||
if( pPage ){
|
||||
@@ -612,8 +774,7 @@ fetch_out:
|
||||
if( pPage && iKey>pCache->iMaxKey ){
|
||||
pCache->iMaxKey = iKey;
|
||||
}
|
||||
if( createFlag==1 ) sqlite3EndBenignMalloc();
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(pGroup);
|
||||
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
|
||||
}
|
||||
|
||||
@@ -626,37 +787,34 @@ fetch_out:
|
||||
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
|
||||
assert( pPage->pCache==pCache );
|
||||
pcache1EnterMutex();
|
||||
pcache1EnterMutex(pGroup);
|
||||
|
||||
/* It is an error to call this function if the page is already
|
||||
** part of the global LRU list.
|
||||
** part of the PGroup LRU list.
|
||||
*/
|
||||
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||
assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
|
||||
assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
|
||||
|
||||
if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){
|
||||
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
/* Add the page to the global LRU list. Normally, the page is added to
|
||||
** the head of the list (last page to be recycled). However, if the
|
||||
** reuseUnlikely flag passed to this function is true, the page is added
|
||||
** to the tail of the list (first page to be recycled).
|
||||
*/
|
||||
if( pcache1.pLruHead ){
|
||||
pcache1.pLruHead->pLruPrev = pPage;
|
||||
pPage->pLruNext = pcache1.pLruHead;
|
||||
pcache1.pLruHead = pPage;
|
||||
/* Add the page to the PGroup LRU list. */
|
||||
if( pGroup->pLruHead ){
|
||||
pGroup->pLruHead->pLruPrev = pPage;
|
||||
pPage->pLruNext = pGroup->pLruHead;
|
||||
pGroup->pLruHead = pPage;
|
||||
}else{
|
||||
pcache1.pLruTail = pPage;
|
||||
pcache1.pLruHead = pPage;
|
||||
pGroup->pLruTail = pPage;
|
||||
pGroup->pLruHead = pPage;
|
||||
}
|
||||
pCache->nRecyclable++;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -675,7 +833,7 @@ static void pcache1Rekey(
|
||||
assert( pPage->iKey==iOld );
|
||||
assert( pPage->pCache==pCache );
|
||||
|
||||
pcache1EnterMutex();
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
|
||||
h = iOld%pCache->nHash;
|
||||
pp = &pCache->apHash[h];
|
||||
@@ -692,7 +850,7 @@ static void pcache1Rekey(
|
||||
pCache->iMaxKey = iNew;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -704,12 +862,12 @@ static void pcache1Rekey(
|
||||
*/
|
||||
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
pcache1EnterMutex();
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
if( iLimit<=pCache->iMaxKey ){
|
||||
pcache1TruncateUnsafe(pCache, iLimit);
|
||||
pCache->iMaxKey = iLimit-1;
|
||||
}
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -719,12 +877,15 @@ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
|
||||
*/
|
||||
static void pcache1Destroy(sqlite3_pcache *p){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
pcache1EnterMutex();
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
|
||||
pcache1EnterMutex(pGroup);
|
||||
pcache1TruncateUnsafe(pCache, 0);
|
||||
pcache1.nMaxPage -= pCache->nMax;
|
||||
pcache1.nMinPage -= pCache->nMin;
|
||||
pcache1EnforceMaxPage();
|
||||
pcache1LeaveMutex();
|
||||
pGroup->nMaxPage -= pCache->nMax;
|
||||
pGroup->nMinPage -= pCache->nMin;
|
||||
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
||||
pcache1EnforceMaxPage(pGroup);
|
||||
pcache1LeaveMutex(pGroup);
|
||||
sqlite3_free(pCache->apHash);
|
||||
sqlite3_free(pCache);
|
||||
}
|
||||
@@ -763,16 +924,18 @@ void sqlite3PCacheSetDefault(void){
|
||||
*/
|
||||
int sqlite3PcacheReleaseMemory(int nReq){
|
||||
int nFree = 0;
|
||||
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
|
||||
assert( sqlite3_mutex_notheld(pcache1.mutex) );
|
||||
if( pcache1.pStart==0 ){
|
||||
PgHdr1 *p;
|
||||
pcache1EnterMutex();
|
||||
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
|
||||
pcache1EnterMutex(&pcache1.grp);
|
||||
while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
|
||||
nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
}
|
||||
pcache1LeaveMutex();
|
||||
pcache1LeaveMutex(&pcache1.grp);
|
||||
}
|
||||
return nFree;
|
||||
}
|
||||
@@ -791,12 +954,12 @@ void sqlite3PcacheStats(
|
||||
){
|
||||
PgHdr1 *p;
|
||||
int nRecyclable = 0;
|
||||
for(p=pcache1.pLruHead; p; p=p->pLruNext){
|
||||
for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
|
||||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.nCurrentPage;
|
||||
*pnMax = pcache1.nMaxPage;
|
||||
*pnMin = pcache1.nMinPage;
|
||||
*pnCurrent = pcache1.grp.nCurrentPage;
|
||||
*pnMax = pcache1.grp.nMaxPage;
|
||||
*pnMin = pcache1.grp.nMinPage;
|
||||
*pnRecyclable = nRecyclable;
|
||||
}
|
||||
#endif
|
||||
|
||||
+29
-36
@@ -35,7 +35,7 @@ static u8 getSafetyLevel(const char *z){
|
||||
static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
|
||||
int i, n;
|
||||
if( sqlite3Isdigit(*z) ){
|
||||
return (u8)atoi(z);
|
||||
return (u8)sqlite3Atoi(z);
|
||||
}
|
||||
n = sqlite3Strlen30(z);
|
||||
for(i=0; i<ArraySize(iLength); i++){
|
||||
@@ -76,7 +76,7 @@ static int getAutoVacuum(const char *z){
|
||||
if( 0==sqlite3StrICmp(z, "none") ) return BTREE_AUTOVACUUM_NONE;
|
||||
if( 0==sqlite3StrICmp(z, "full") ) return BTREE_AUTOVACUUM_FULL;
|
||||
if( 0==sqlite3StrICmp(z, "incremental") ) return BTREE_AUTOVACUUM_INCR;
|
||||
i = atoi(z);
|
||||
i = sqlite3Atoi(z);
|
||||
return (u8)((i>=0&&i<=2)?i:0);
|
||||
}
|
||||
#endif /* ifndef SQLITE_OMIT_AUTOVACUUM */
|
||||
@@ -172,6 +172,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
||||
{ "empty_result_callbacks", SQLITE_NullCallback },
|
||||
{ "legacy_file_format", SQLITE_LegacyFileFmt },
|
||||
{ "fullfsync", SQLITE_FullFSync },
|
||||
{ "checkpoint_fullfsync", SQLITE_CkptFullFSync },
|
||||
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
{ "automatic_index", SQLITE_AutoIndex },
|
||||
@@ -383,7 +384,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeChangeP1(v, addr+1, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
}else{
|
||||
int size = atoi(zRight);
|
||||
int size = sqlite3Atoi(zRight);
|
||||
if( size<0 ) size = -size;
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
|
||||
@@ -412,35 +413,13 @@ void sqlite3Pragma(
|
||||
/* Malloc may fail when setting the page-size, as there is an internal
|
||||
** buffer that the pager module resizes using sqlite3_realloc().
|
||||
*/
|
||||
db->nextPagesize = atoi(zRight);
|
||||
db->nextPagesize = sqlite3Atoi(zRight);
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA [database.]max_page_count
|
||||
** PRAGMA [database.]max_page_count=N
|
||||
**
|
||||
** The first form reports the current setting for the
|
||||
** maximum number of pages in the database file. The
|
||||
** second form attempts to change this setting. Both
|
||||
** forms return the current setting.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"max_page_count")==0 ){
|
||||
Btree *pBt = pDb->pBt;
|
||||
int newMax = 0;
|
||||
assert( pBt!=0 );
|
||||
if( zRight ){
|
||||
newMax = atoi(zRight);
|
||||
}
|
||||
if( ALWAYS(pBt) ){
|
||||
newMax = sqlite3BtreeMaxPageCount(pBt, newMax);
|
||||
}
|
||||
returnSingleInt(pParse, "max_page_count", newMax);
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA [database.]secure_delete
|
||||
** PRAGMA [database.]secure_delete=ON/OFF
|
||||
@@ -467,19 +446,33 @@ void sqlite3Pragma(
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA [database.]max_page_count
|
||||
** PRAGMA [database.]max_page_count=N
|
||||
**
|
||||
** The first form reports the current setting for the
|
||||
** maximum number of pages in the database file. The
|
||||
** second form attempts to change this setting. Both
|
||||
** forms return the current setting.
|
||||
**
|
||||
** PRAGMA [database.]page_count
|
||||
**
|
||||
** Return the number of pages in the specified database.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"page_count")==0 ){
|
||||
if( sqlite3StrICmp(zLeft,"page_count")==0
|
||||
|| sqlite3StrICmp(zLeft,"max_page_count")==0
|
||||
){
|
||||
int iReg;
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
iReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
|
||||
if( zLeft[0]=='p' ){
|
||||
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight));
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "page_count", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
|
||||
}else
|
||||
|
||||
/*
|
||||
@@ -587,7 +580,7 @@ void sqlite3Pragma(
|
||||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
i64 iLimit = -2;
|
||||
if( zRight ){
|
||||
sqlite3Atoi64(zRight, &iLimit);
|
||||
sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
|
||||
if( iLimit<-1 ) iLimit = -1;
|
||||
}
|
||||
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
|
||||
@@ -701,7 +694,7 @@ void sqlite3Pragma(
|
||||
if( !zRight ){
|
||||
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
|
||||
}else{
|
||||
int size = atoi(zRight);
|
||||
int size = sqlite3Atoi(zRight);
|
||||
if( size<0 ) size = -size;
|
||||
pDb->pSchema->cache_size = size;
|
||||
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
|
||||
@@ -1094,7 +1087,7 @@ void sqlite3Pragma(
|
||||
/* Set the maximum error count */
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
if( zRight ){
|
||||
mxErr = atoi(zRight);
|
||||
sqlite3GetInt32(zRight, &mxErr);
|
||||
if( mxErr<=0 ){
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
}
|
||||
@@ -1351,7 +1344,7 @@ void sqlite3Pragma(
|
||||
};
|
||||
int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie);
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+1, atoi(zRight));
|
||||
sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
|
||||
sqlite3VdbeChangeP1(v, addr+2, iDb);
|
||||
sqlite3VdbeChangeP2(v, addr+2, iCookie);
|
||||
}else{
|
||||
@@ -1412,8 +1405,7 @@ void sqlite3Pragma(
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
|
||||
if( zRight ){
|
||||
int nAuto = atoi(zRight);
|
||||
sqlite3_wal_autocheckpoint(db, nAuto);
|
||||
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
|
||||
}
|
||||
returnSingleInt(pParse, "wal_autocheckpoint",
|
||||
db->xWalCallback==sqlite3WalDefaultHook ?
|
||||
@@ -1533,7 +1525,8 @@ void sqlite3Pragma(
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
if( db->autoCommit ){
|
||||
sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
|
||||
(db->flags&SQLITE_FullFSync)!=0);
|
||||
(db->flags&SQLITE_FullFSync)!=0,
|
||||
(db->flags&SQLITE_CkptFullFSync)!=0);
|
||||
}
|
||||
#endif
|
||||
pragma_out:
|
||||
|
||||
+7
-7
@@ -79,7 +79,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = iDb;
|
||||
db->init.newTnum = atoi(argv[1]);
|
||||
db->init.newTnum = sqlite3Atoi(argv[1]);
|
||||
db->init.orphanTrigger = 0;
|
||||
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
|
||||
rc = db->errCode;
|
||||
@@ -628,13 +628,13 @@ static int sqlite3Prepare(
|
||||
if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){
|
||||
static const char * const azColName[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
||||
"order", "from", "detail"
|
||||
"selectid", "order", "from", "detail"
|
||||
};
|
||||
int iFirst, mx;
|
||||
if( pParse->explain==2 ){
|
||||
sqlite3VdbeSetNumCols(pParse->pVdbe, 3);
|
||||
sqlite3VdbeSetNumCols(pParse->pVdbe, 4);
|
||||
iFirst = 8;
|
||||
mx = 11;
|
||||
mx = 12;
|
||||
}else{
|
||||
sqlite3VdbeSetNumCols(pParse->pVdbe, 8);
|
||||
iFirst = 0;
|
||||
@@ -784,7 +784,7 @@ int sqlite3_prepare_v2(
|
||||
*/
|
||||
static int sqlite3Prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
const void *zSql, /* UTF-16 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
@@ -834,7 +834,7 @@ static int sqlite3Prepare16(
|
||||
*/
|
||||
int sqlite3_prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
const void *zSql, /* UTF-16 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
@@ -846,7 +846,7 @@ int sqlite3_prepare16(
|
||||
}
|
||||
int sqlite3_prepare16_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
const void *zSql, /* UTF-16 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
|
||||
+20
-13
@@ -763,6 +763,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
||||
i64 szNew = p->nChar;
|
||||
szNew += N + 1;
|
||||
if( szNew > p->mxAlloc ){
|
||||
@@ -773,13 +774,12 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
p->nAlloc = (int)szNew;
|
||||
}
|
||||
if( p->useMalloc==1 ){
|
||||
zNew = sqlite3DbMallocRaw(p->db, p->nAlloc );
|
||||
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
||||
}else{
|
||||
zNew = sqlite3_malloc(p->nAlloc);
|
||||
zNew = sqlite3_realloc(zOld, p->nAlloc);
|
||||
}
|
||||
if( zNew ){
|
||||
memcpy(zNew, p->zText, p->nChar);
|
||||
sqlite3StrAccumReset(p);
|
||||
if( zOld==0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
p->mallocFailed = 1;
|
||||
@@ -934,21 +934,28 @@ char *sqlite3_mprintf(const char *zFormat, ...){
|
||||
** current locale settings. This is important for SQLite because we
|
||||
** are not able to use a "," as the decimal point in place of "." as
|
||||
** specified by some locales.
|
||||
**
|
||||
** Oops: The first two arguments of sqlite3_snprintf() are backwards
|
||||
** from the snprintf() standard. Unfortunately, it is too late to change
|
||||
** this without breaking compatibility, so we just have to live with the
|
||||
** mistake.
|
||||
**
|
||||
** sqlite3_vsnprintf() is the varargs version.
|
||||
*/
|
||||
char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
||||
StrAccum acc;
|
||||
if( n<=0 ) return zBuf;
|
||||
sqlite3StrAccumInit(&acc, zBuf, n, 0);
|
||||
acc.useMalloc = 0;
|
||||
sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
||||
return sqlite3StrAccumFinish(&acc);
|
||||
}
|
||||
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
char *z;
|
||||
va_list ap;
|
||||
StrAccum acc;
|
||||
|
||||
if( n<=0 ){
|
||||
return zBuf;
|
||||
}
|
||||
sqlite3StrAccumInit(&acc, zBuf, n, 0);
|
||||
acc.useMalloc = 0;
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
||||
z = sqlite3_vsnprintf(n, zBuf, zFormat, ap);
|
||||
va_end(ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
+164
-46
@@ -442,7 +442,6 @@ static void pushOntoSorter(
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
pSelect->iLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,11 +490,13 @@ 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 occurs in multiple
|
||||
** places.
|
||||
** 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. */
|
||||
@@ -511,6 +512,7 @@ static int checkForMultiColumnSelectError(
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine generates the code for the inside of the inner loop
|
||||
@@ -590,10 +592,6 @@ static void selectInnerLoop(
|
||||
}
|
||||
}
|
||||
|
||||
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
|
||||
return;
|
||||
}
|
||||
|
||||
switch( eDest ){
|
||||
/* In this mode, write each query result to the key of the temporary
|
||||
** table iParm.
|
||||
@@ -722,11 +720,11 @@ static void selectInnerLoop(
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Jump to the end of the loop if the LIMIT is reached.
|
||||
/* Jump to the end of the loop if the LIMIT is reached. Except, if
|
||||
** there is a sorter, in which case the sorter has already limited
|
||||
** the output for us.
|
||||
*/
|
||||
if( p->iLimit ){
|
||||
assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to
|
||||
** pushOntoSorter() would have cleared p->iLimit */
|
||||
if( pOrderBy==0 && p->iLimit ){
|
||||
sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
|
||||
}
|
||||
}
|
||||
@@ -773,6 +771,88 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
/*
|
||||
** Name of the connection operator, used for error messages.
|
||||
*/
|
||||
static const char *selectOpName(int id){
|
||||
char *z;
|
||||
switch( id ){
|
||||
case TK_ALL: z = "UNION ALL"; break;
|
||||
case TK_INTERSECT: z = "INTERSECT"; break;
|
||||
case TK_EXCEPT: z = "EXCEPT"; break;
|
||||
default: z = "UNION"; break;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/*
|
||||
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
|
||||
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
|
||||
** where the caption is of the form:
|
||||
**
|
||||
** "USE TEMP B-TREE FOR xxx"
|
||||
**
|
||||
** where xxx is one of "DISTINCT", "ORDER BY" or "GROUP BY". Exactly which
|
||||
** is determined by the zUsage argument.
|
||||
*/
|
||||
static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
|
||||
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
|
||||
** where the caption is of one of the two forms:
|
||||
**
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
|
||||
**
|
||||
** where iSub1 and iSub2 are the integers passed as the corresponding
|
||||
** function parameters, and op is the text representation of the parameter
|
||||
** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
|
||||
** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
|
||||
** false, or the second form if it is true.
|
||||
*/
|
||||
static void explainComposite(
|
||||
Parse *pParse, /* Parse context */
|
||||
int op, /* One of TK_UNION, TK_EXCEPT etc. */
|
||||
int iSub1, /* Subquery id 1 */
|
||||
int iSub2, /* Subquery id 2 */
|
||||
int bUseTmp /* True if a temp table was used */
|
||||
){
|
||||
assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
|
||||
bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Assign expression b to lvalue a. A second, no-op, version of this macro
|
||||
** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code
|
||||
** in sqlite3Select() to assign values to structure member variables that
|
||||
** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the
|
||||
** code with #ifndef directives.
|
||||
*/
|
||||
# define explainSetInteger(a, b) a = b
|
||||
|
||||
#else
|
||||
/* No-op versions of the explainXXX() functions and macros. */
|
||||
# define explainTempTable(y,z)
|
||||
# define explainComposite(v,w,x,y,z)
|
||||
# define explainSetInteger(y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the inner loop was generated using a non-null pOrderBy argument,
|
||||
@@ -861,10 +941,6 @@ static void generateSortTail(
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
|
||||
/* LIMIT has been implemented by the pushOntoSorter() routine.
|
||||
*/
|
||||
assert( p->iLimit==0 );
|
||||
|
||||
/* The bottom of the loop
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrContinue);
|
||||
@@ -1124,22 +1200,6 @@ static void generateColumnNames(
|
||||
generateColumnTypes(pParse, pTabList, pEList);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
/*
|
||||
** Name of the connection operator, used for error messages.
|
||||
*/
|
||||
static const char *selectOpName(int id){
|
||||
char *z;
|
||||
switch( id ){
|
||||
case TK_ALL: z = "UNION ALL"; break;
|
||||
case TK_INTERSECT: z = "INTERSECT"; break;
|
||||
case TK_EXCEPT: z = "EXCEPT"; break;
|
||||
default: z = "UNION"; break;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
||||
|
||||
/*
|
||||
** Given a an expression list (which is really the list of expressions
|
||||
** that form the result set of a SELECT statement) compute appropriate
|
||||
@@ -1302,6 +1362,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
assert( db->lookaside.bEnabled==0 );
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = 0;
|
||||
pTab->nRowEst = 1000000;
|
||||
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
|
||||
pTab->iPKey = -1;
|
||||
@@ -1372,6 +1433,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
||||
VdbeComment((v, "LIMIT counter"));
|
||||
if( n==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
|
||||
}else{
|
||||
if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n;
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, p->pLimit, iLimit);
|
||||
@@ -1472,6 +1535,10 @@ static int multiSelect(
|
||||
SelectDest dest; /* Alternative data destination */
|
||||
Select *pDelete = 0; /* Chain of simple selects to delete */
|
||||
sqlite3 *db; /* Database connection */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1; /* EQP id of left-hand query */
|
||||
int iSub2; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
||||
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
||||
@@ -1503,6 +1570,7 @@ static int multiSelect(
|
||||
if( dest.eDest==SRT_EphemTab ){
|
||||
assert( p->pEList );
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
dest.eDest = SRT_Table;
|
||||
}
|
||||
|
||||
@@ -1528,9 +1596,11 @@ static int multiSelect(
|
||||
switch( p->op ){
|
||||
case TK_ALL: {
|
||||
int addr = 0;
|
||||
int nLimit;
|
||||
assert( !pPrior->pLimit );
|
||||
pPrior->pLimit = p->pLimit;
|
||||
pPrior->pOffset = p->pOffset;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &dest);
|
||||
p->pLimit = 0;
|
||||
p->pOffset = 0;
|
||||
@@ -1544,10 +1614,18 @@ static int multiSelect(
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
|
||||
VdbeComment((v, "Jump ahead if LIMIT reached"));
|
||||
}
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->nSelectRow += pPrior->nSelectRow;
|
||||
if( pPrior->pLimit
|
||||
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
|
||||
&& p->nSelectRow > (double)nLimit
|
||||
){
|
||||
p->nSelectRow = (double)nLimit;
|
||||
}
|
||||
if( addr ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
@@ -1591,6 +1669,7 @@ static int multiSelect(
|
||||
*/
|
||||
assert( !pPrior->pOrderBy );
|
||||
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &uniondest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@@ -1610,6 +1689,7 @@ static int multiSelect(
|
||||
pOffset = p->pOffset;
|
||||
p->pOffset = 0;
|
||||
uniondest.eDest = op;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &uniondest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
||||
@@ -1618,6 +1698,7 @@ static int multiSelect(
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->pOrderBy = 0;
|
||||
if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow;
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->pOffset = pOffset;
|
||||
@@ -1675,6 +1756,7 @@ static int multiSelect(
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@@ -1691,10 +1773,12 @@ static int multiSelect(
|
||||
pOffset = p->pOffset;
|
||||
p->pOffset = 0;
|
||||
intersectdest.iParm = tab2;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &intersectdest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->pOffset = pOffset;
|
||||
@@ -1727,6 +1811,8 @@ static int multiSelect(
|
||||
}
|
||||
}
|
||||
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
|
||||
|
||||
/* Compute collating sequences used by
|
||||
** temporary tables needed to implement the compound select.
|
||||
** Attach the KeyInfo structure to all temporary tables.
|
||||
@@ -2070,6 +2156,10 @@ static int multiSelectOrderBy(
|
||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||
int nOrderBy; /* Number of terms in the ORDER BY clause */
|
||||
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1; /* EQP id of left-hand query */
|
||||
int iSub2; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
assert( p->pOrderBy!=0 );
|
||||
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
|
||||
@@ -2181,7 +2271,6 @@ static int multiSelectOrderBy(
|
||||
/* Separate the left and the right query from one another
|
||||
*/
|
||||
p->pPrior = 0;
|
||||
pPrior->pRightmost = 0;
|
||||
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
|
||||
if( pPrior->pPrior==0 ){
|
||||
sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
|
||||
@@ -2224,6 +2313,7 @@ static int multiSelectOrderBy(
|
||||
*/
|
||||
VdbeNoopComment((v, "Begin coroutine for left SELECT"));
|
||||
pPrior->iLimit = regLimitA;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pPrior, &destA);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
|
||||
sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
|
||||
@@ -2238,6 +2328,7 @@ static int multiSelectOrderBy(
|
||||
savedOffset = p->iOffset;
|
||||
p->iLimit = regLimitB;
|
||||
p->iOffset = 0;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, p, &destB);
|
||||
p->iLimit = savedLimit;
|
||||
p->iOffset = savedOffset;
|
||||
@@ -2274,6 +2365,7 @@ static int multiSelectOrderBy(
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
|
||||
sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
|
||||
p->nSelectRow += pPrior->nSelectRow;
|
||||
}
|
||||
|
||||
/* Generate a subroutine to run when the results from select B
|
||||
@@ -2281,6 +2373,7 @@ static int multiSelectOrderBy(
|
||||
*/
|
||||
if( op==TK_INTERSECT ){
|
||||
addrEofB = addrEofA;
|
||||
if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
|
||||
}else{
|
||||
VdbeNoopComment((v, "eof-B subroutine"));
|
||||
addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd);
|
||||
@@ -2368,6 +2461,7 @@ static int multiSelectOrderBy(
|
||||
|
||||
/*** TBD: Insert subroutine calls to close cursors on incomplete
|
||||
**** subqueries ****/
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
@@ -3101,6 +3195,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowEst = 1000000;
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
#endif
|
||||
}else{
|
||||
@@ -3465,7 +3560,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
if( pList ){
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0);
|
||||
sqlite3ExprCodeExprList(pParse, pList, regAgg, 1);
|
||||
}else{
|
||||
nArg = 0;
|
||||
regAgg = 0;
|
||||
@@ -3594,6 +3689,11 @@ int sqlite3Select(
|
||||
int iEnd; /* Address of the end of the query */
|
||||
sqlite3 *db; /* The database connection */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iRestoreSelectId = pParse->iSelectId;
|
||||
pParse->iSelectId = pParse->iNextSelectId++;
|
||||
#endif
|
||||
|
||||
db = pParse->db;
|
||||
if( p==0 || db->mallocFailed || pParse->nErr ){
|
||||
return 1;
|
||||
@@ -3625,6 +3725,15 @@ int sqlite3Select(
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto select_end;
|
||||
|
||||
/* If writing to memory or generating a set
|
||||
** only a single column may be output.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
|
||||
goto select_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
@@ -3656,8 +3765,10 @@ int sqlite3Select(
|
||||
}else{
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
assert( pItem->isPopulated==0 );
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->isPopulated = 1;
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
}
|
||||
if( /*pParse->nErr ||*/ db->mallocFailed ){
|
||||
goto select_end;
|
||||
@@ -3691,19 +3802,12 @@ int sqlite3Select(
|
||||
mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
|
||||
if( mxSelect && cnt>mxSelect ){
|
||||
sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
|
||||
return 1;
|
||||
goto select_end;
|
||||
}
|
||||
}
|
||||
return multiSelect(pParse, p, pDest);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If writing to memory or generating a set
|
||||
** only a single column may be output.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
|
||||
goto select_end;
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3715,7 +3819,6 @@ int sqlite3Select(
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
isDistinct = 0;
|
||||
}
|
||||
|
||||
/* If there is both a GROUP BY and an ORDER BY clause and they are
|
||||
@@ -3758,17 +3861,19 @@ int sqlite3Select(
|
||||
/* Set the limiter.
|
||||
*/
|
||||
iEnd = sqlite3VdbeMakeLabel(v);
|
||||
p->nSelectRow = (double)LARGEST_INT64;
|
||||
computeLimitRegisters(pParse, p, iEnd);
|
||||
|
||||
/* Open a virtual index to use for the distinct set.
|
||||
*/
|
||||
if( isDistinct ){
|
||||
if( p->selFlags & SF_Distinct ){
|
||||
KeyInfo *pKeyInfo;
|
||||
assert( isAgg || pGroupBy );
|
||||
distinct = pParse->nTab++;
|
||||
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
}else{
|
||||
distinct = -1;
|
||||
}
|
||||
@@ -3780,6 +3885,7 @@ int sqlite3Select(
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
|
||||
|
||||
/* If sorting index that was created by a prior OP_OpenEphemeral
|
||||
** instruction ended up not being needed, then change the OP_OpenEphemeral
|
||||
@@ -3824,6 +3930,9 @@ int sqlite3Select(
|
||||
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
|
||||
pItem->iAlias = 0;
|
||||
}
|
||||
if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100;
|
||||
}else{
|
||||
p->nSelectRow = (double)1;
|
||||
}
|
||||
|
||||
|
||||
@@ -3920,6 +4029,9 @@ int sqlite3Select(
|
||||
int nCol;
|
||||
int nGroupBy;
|
||||
|
||||
explainTempTable(pParse,
|
||||
isDistinct && !(p->selFlags&SF_Distinct)?"DISTINCT":"GROUP BY");
|
||||
|
||||
groupBySort = 1;
|
||||
nGroupBy = pGroupBy->nExpr;
|
||||
nCol = nGroupBy + 1;
|
||||
@@ -4181,10 +4293,15 @@ int sqlite3Select(
|
||||
|
||||
} /* endif aggregate query */
|
||||
|
||||
if( distinct>=0 ){
|
||||
explainTempTable(pParse, "DISTINCT");
|
||||
}
|
||||
|
||||
/* If there is an ORDER BY clause, then we need to sort the results
|
||||
** and send them to the callback one by one.
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
explainTempTable(pParse, "ORDER BY");
|
||||
generateSortTail(pParse, p, v, pEList->nExpr, pDest);
|
||||
}
|
||||
|
||||
@@ -4201,6 +4318,7 @@ int sqlite3Select(
|
||||
** successful coding of the SELECT.
|
||||
*/
|
||||
select_end:
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
|
||||
/* Identify column names if results of the SELECT are to be output.
|
||||
*/
|
||||
|
||||
+32
-7
@@ -38,10 +38,14 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
# include <editline/editline.h>
|
||||
#endif
|
||||
#if defined(HAVE_READLINE) && HAVE_READLINE==1
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#else
|
||||
#endif
|
||||
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
|
||||
# define readline(p) local_getline(p,stdin)
|
||||
# define add_history(X)
|
||||
# define read_history(X)
|
||||
@@ -980,7 +984,7 @@ static int display_stats(
|
||||
fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Allocations: %d (max %d)\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr);
|
||||
/*
|
||||
** Not currently used by the CLI.
|
||||
** iHiwtr = iCur = -1;
|
||||
@@ -1019,6 +1023,12 @@ static int display_stats(
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur);
|
||||
@@ -2538,6 +2548,7 @@ int main(int argc, char **argv){
|
||||
|
||||
/* Do an initial pass through the command-line argument to locate
|
||||
** the name of the database file, the name of the initialization file,
|
||||
** the size of the alternative malloc heap,
|
||||
** and the first command to execute.
|
||||
*/
|
||||
for(i=1; i<argc-1; i++){
|
||||
@@ -2556,6 +2567,22 @@ int main(int argc, char **argv){
|
||||
*/
|
||||
}else if( strcmp(argv[i],"-batch")==0 ){
|
||||
stdin_is_interactive = 0;
|
||||
}else if( strcmp(argv[i],"-heap")==0 ){
|
||||
int j, c;
|
||||
const char *zSize;
|
||||
sqlite3_int64 szHeap;
|
||||
|
||||
zSize = argv[++i];
|
||||
szHeap = atoi(zSize);
|
||||
for(j=0; (c = zSize[j])!=0; j++){
|
||||
if( c=='M' ){ szHeap *= 1000000; break; }
|
||||
if( c=='K' ){ szHeap *= 1000; break; }
|
||||
if( c=='G' ){ szHeap *= 1000000000; break; }
|
||||
}
|
||||
if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if( i<argc ){
|
||||
@@ -2662,6 +2689,8 @@ int main(int argc, char **argv){
|
||||
stdin_is_interactive = 1;
|
||||
}else if( strcmp(z,"-batch")==0 ){
|
||||
stdin_is_interactive = 0;
|
||||
}else if( strcmp(z,"-heap")==0 ){
|
||||
i++;
|
||||
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
|
||||
usage(1);
|
||||
}else{
|
||||
@@ -2723,11 +2752,7 @@ int main(int argc, char **argv){
|
||||
}
|
||||
set_table_name(&data, 0);
|
||||
if( data.db ){
|
||||
if( sqlite3_close(data.db)!=SQLITE_OK ){
|
||||
fprintf(stderr,"Error: cannot close database \"%s\"\n",
|
||||
sqlite3_errmsg(db));
|
||||
rc++;
|
||||
}
|
||||
sqlite3_close(data.db);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+581
-271
File diff suppressed because it is too large
Load Diff
@@ -191,6 +191,27 @@ struct sqlite3_api_routines {
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -370,6 +391,27 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#endif /* SQLITE_CORE */
|
||||
|
||||
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
|
||||
|
||||
+59
-17
@@ -114,15 +114,21 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The SQLITE_THREADSAFE macro must be defined as either 0 or 1.
|
||||
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
|
||||
** 0 means mutexes are permanently disable and the library is never
|
||||
** threadsafe. 1 means the library is serialized which is the highest
|
||||
** level of threadsafety. 2 means the libary is multithreaded - multiple
|
||||
** threads can use SQLite as long as no two threads try to use the same
|
||||
** database connection at the same time.
|
||||
**
|
||||
** Older versions of SQLite used an optional THREADSAFE macro.
|
||||
** We support that for legacy
|
||||
** We support that for legacy.
|
||||
*/
|
||||
#if !defined(SQLITE_THREADSAFE)
|
||||
#if defined(THREADSAFE)
|
||||
# define SQLITE_THREADSAFE THREADSAFE
|
||||
#else
|
||||
# define SQLITE_THREADSAFE 1
|
||||
# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -600,6 +606,7 @@ typedef struct Expr Expr;
|
||||
typedef struct ExprList ExprList;
|
||||
typedef struct ExprSpan ExprSpan;
|
||||
typedef struct FKey FKey;
|
||||
typedef struct FuncDestructor FuncDestructor;
|
||||
typedef struct FuncDef FuncDef;
|
||||
typedef struct FuncDefHash FuncDefHash;
|
||||
typedef struct IdList IdList;
|
||||
@@ -730,6 +737,7 @@ struct Lookaside {
|
||||
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
|
||||
int nOut; /* Number of buffers currently checked out */
|
||||
int mxOut; /* Highwater mark for nOut */
|
||||
int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */
|
||||
LookasideSlot *pFree; /* List of available buffers */
|
||||
void *pStart; /* First byte of available memory space */
|
||||
void *pEnd; /* First byte past end of available space */
|
||||
@@ -808,6 +816,7 @@ struct sqlite3 {
|
||||
struct Vdbe *pVdbe; /* List of active virtual machines */
|
||||
int activeVdbeCnt; /* Number of VDBEs currently executing */
|
||||
int writeVdbeCnt; /* Number of active VDBEs that are writing */
|
||||
int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
|
||||
void (*xTrace)(void*,const char*); /* Trace function */
|
||||
void *pTraceArg; /* Argument to the trace function */
|
||||
void (*xProfile)(void*,const char*,u64); /* Profiling function */
|
||||
@@ -907,13 +916,14 @@ struct sqlite3 {
|
||||
#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
|
||||
#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
|
||||
#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */
|
||||
#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
|
||||
#define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */
|
||||
#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */
|
||||
#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */
|
||||
#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */
|
||||
#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */
|
||||
#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
|
||||
#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
|
||||
#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
|
||||
|
||||
/*
|
||||
** Bits of the sqlite3.flags field that are used by the
|
||||
@@ -926,6 +936,7 @@ struct sqlite3 {
|
||||
#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */
|
||||
#define SQLITE_IndexCover 0x10 /* Disable index covering table */
|
||||
#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
|
||||
#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
|
||||
#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
|
||||
|
||||
/*
|
||||
@@ -956,6 +967,27 @@ struct FuncDef {
|
||||
void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */
|
||||
char *zName; /* SQL name of the function. */
|
||||
FuncDef *pHash; /* Next with a different name but the same hash */
|
||||
FuncDestructor *pDestructor; /* Reference counted destructor function */
|
||||
};
|
||||
|
||||
/*
|
||||
** This structure encapsulates a user-function destructor callback (as
|
||||
** configured using create_function_v2()) and a reference counter. When
|
||||
** create_function_v2() is called to create a function with a destructor,
|
||||
** a single object of this type is allocated. FuncDestructor.nRef is set to
|
||||
** the number of FuncDef objects created (either 1 or 3, depending on whether
|
||||
** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor
|
||||
** member of each of the new FuncDef objects is set to point to the allocated
|
||||
** FuncDestructor.
|
||||
**
|
||||
** Thereafter, when one of the FuncDef objects is deleted, the reference
|
||||
** count on this object is decremented. When it reaches 0, the destructor
|
||||
** is invoked and the FuncDestructor structure freed.
|
||||
*/
|
||||
struct FuncDestructor {
|
||||
int nRef;
|
||||
void (*xDestroy)(void *);
|
||||
void *pUserData;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -996,15 +1028,15 @@ struct FuncDef {
|
||||
*/
|
||||
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0}
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
|
||||
pArg, 0, xFunc, 0, 0, #zName, 0}
|
||||
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
#define LIKEFUNC(zName, nArg, arg, flags) \
|
||||
{nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0}
|
||||
{nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
|
||||
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
|
||||
{nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
|
||||
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0}
|
||||
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
|
||||
|
||||
/*
|
||||
** All current savepoints are stored in a linked list starting at
|
||||
@@ -1224,6 +1256,7 @@ struct Table {
|
||||
Column *aCol; /* Information about each column */
|
||||
Index *pIndex; /* List of SQL indexes on this table. */
|
||||
int tnum; /* Root BTree node for this table (see note above) */
|
||||
unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
Select *pSelect; /* NULL for tables. Points to definition if a view. */
|
||||
u16 nRef; /* Number of pointers to this Table */
|
||||
u8 tabFlags; /* Mask of TF_* values */
|
||||
@@ -1792,6 +1825,9 @@ struct SrcList {
|
||||
u8 isPopulated; /* Temporary table associated with SELECT is populated */
|
||||
u8 jointype; /* Type of join between this able and the previous */
|
||||
u8 notIndexed; /* True if there is a NOT INDEXED clause */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
|
||||
#endif
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
Expr *pOn; /* The ON clause of a join */
|
||||
IdList *pUsing; /* The USING clause of a join */
|
||||
@@ -1830,6 +1866,7 @@ struct SrcList {
|
||||
struct WherePlan {
|
||||
u32 wsFlags; /* WHERE_* flags that describe the strategy */
|
||||
u32 nEq; /* Number of == constraints */
|
||||
double nRow; /* Estimated number of rows (for EQP) */
|
||||
union {
|
||||
Index *pIdx; /* Index when WHERE_INDEXED is true */
|
||||
struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
|
||||
@@ -1914,6 +1951,7 @@ struct WhereInfo {
|
||||
int nLevel; /* Number of nested loop */
|
||||
struct WhereClause *pWC; /* Decomposition of the WHERE clause */
|
||||
double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
|
||||
double nRowOut; /* Estimated number of output rows */
|
||||
WhereLevel a[1]; /* Information about each nest loop in WHERE */
|
||||
};
|
||||
|
||||
@@ -1989,6 +2027,7 @@ struct Select {
|
||||
Expr *pOffset; /* OFFSET expression. NULL means not used. */
|
||||
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
||||
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
|
||||
double nSelectRow; /* Estimated number of result rows */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2184,6 +2223,11 @@ struct Parse {
|
||||
int nHeight; /* Expression tree height of current sub-select */
|
||||
Table *pZombieTab; /* List of Table objects to delete after code gen */
|
||||
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSelectId;
|
||||
int iNextSelectId;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SQLITE_OMIT_VIRTUALTABLE
|
||||
@@ -2478,7 +2522,6 @@ int sqlite3CantopenError(int);
|
||||
** Internal function prototypes
|
||||
*/
|
||||
int sqlite3StrICmp(const char *, const char *);
|
||||
int sqlite3IsNumber(const char*, int*, u8);
|
||||
int sqlite3Strlen30(const char*);
|
||||
#define sqlite3StrNICmp sqlite3_strnicmp
|
||||
|
||||
@@ -2502,7 +2545,7 @@ void *sqlite3PageMalloc(int);
|
||||
void sqlite3PageFree(void*);
|
||||
void sqlite3MemSetDefault(void);
|
||||
void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
|
||||
int sqlite3MemoryAlarm(void (*)(void*, sqlite3_int64, int), void*, sqlite3_int64);
|
||||
int sqlite3HeapNearlyFull(void);
|
||||
|
||||
/*
|
||||
** On systems with ample stack space and that support alloca(), make
|
||||
@@ -2673,7 +2716,6 @@ void sqlite3ExprCachePop(Parse*, int);
|
||||
void sqlite3ExprCacheRemove(Parse*, int, int);
|
||||
void sqlite3ExprCacheClear(Parse*);
|
||||
void sqlite3ExprCacheAffinityChange(Parse*, int, int);
|
||||
void sqlite3ExprHardCopy(Parse*,int,int);
|
||||
int sqlite3ExprCode(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
|
||||
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
|
||||
@@ -2793,17 +2835,15 @@ void sqlite3DeferForeignKey(Parse*, int);
|
||||
#endif
|
||||
void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
|
||||
void sqlite3Detach(Parse*, Expr*);
|
||||
int sqlite3BtreeFactory(sqlite3 *db, const char *zFilename,
|
||||
int omitJournal, int nCache, int flags, Btree **ppBtree);
|
||||
int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
|
||||
int sqlite3FixSrcList(DbFixer*, SrcList*);
|
||||
int sqlite3FixSelect(DbFixer*, Select*);
|
||||
int sqlite3FixExpr(DbFixer*, Expr*);
|
||||
int sqlite3FixExprList(DbFixer*, ExprList*);
|
||||
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
|
||||
int sqlite3AtoF(const char *z, double*);
|
||||
int sqlite3AtoF(const char *z, double*, int, u8);
|
||||
int sqlite3GetInt32(const char *, int*);
|
||||
int sqlite3FitsIn64Bits(const char *, int);
|
||||
int sqlite3Atoi(const char*);
|
||||
int sqlite3Utf16ByteLen(const void *pData, int nChar);
|
||||
int sqlite3Utf8CharLen(const char *pData, int nByte);
|
||||
int sqlite3Utf8Read(const u8*, const u8**);
|
||||
@@ -2849,7 +2889,7 @@ void sqlite3TableAffinityStr(Vdbe *, Table *);
|
||||
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
|
||||
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
|
||||
char sqlite3ExprAffinity(Expr *pExpr);
|
||||
int sqlite3Atoi64(const char*, i64*);
|
||||
int sqlite3Atoi64(const char*, i64*, int, u8);
|
||||
void sqlite3Error(sqlite3*, int, const char*,...);
|
||||
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
|
||||
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
|
||||
@@ -2920,7 +2960,9 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
|
||||
KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
|
||||
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
|
||||
void (*)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*));
|
||||
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
|
||||
FuncDestructor *pDestructor
|
||||
);
|
||||
int sqlite3ApiExit(sqlite3 *db, int);
|
||||
int sqlite3OpenTempDatabase(Parse *);
|
||||
|
||||
|
||||
@@ -116,6 +116,22 @@ int sqlite3_db_status(
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_HIT:
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE:
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
|
||||
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
|
||||
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
|
||||
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
|
||||
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
|
||||
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
|
||||
*pCurrent = 0;
|
||||
*pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
|
||||
if( resetFlag ){
|
||||
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an approximation for the amount of memory currently used
|
||||
** by all pagers associated with the given database connection. The
|
||||
|
||||
+32
-9
@@ -670,6 +670,7 @@ static void DbUpdateHandler(
|
||||
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1));
|
||||
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid));
|
||||
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
}
|
||||
|
||||
static void tclCollateNeeded(
|
||||
@@ -3014,22 +3015,31 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** if the extension only supplies one new name!) The "sqlite" command is
|
||||
** used to open a new SQLite database. See the DbMain() routine above
|
||||
** for additional information.
|
||||
**
|
||||
** The EXTERN macros are required by TCL in order to work on windows.
|
||||
*/
|
||||
int Sqlite3_Init(Tcl_Interp *interp){
|
||||
EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
||||
Tcl_InitStubs(interp, "8.4", 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
|
||||
Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
|
||||
|
||||
#ifndef SQLITE_3_SUFFIX_ONLY
|
||||
/* The "sqlite" alias is undocumented. It is here only to support
|
||||
** legacy scripts. All new scripts should use only the "sqlite3"
|
||||
** command.
|
||||
*/
|
||||
Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
|
||||
Tcl_PkgProvide(interp, "sqlite", PACKAGE_VERSION);
|
||||
#endif
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
|
||||
|
||||
#ifndef SQLITE_3_SUFFIX_ONLY
|
||||
@@ -3567,6 +3577,15 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
||||
extern int Sqlitequota_Init(Tcl_Interp*);
|
||||
extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
||||
extern int SqliteSuperlock_Init(Tcl_Interp*);
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
extern int Zipvfs_Init(Tcl_Interp*);
|
||||
Zipvfs_Init(interp);
|
||||
#endif
|
||||
|
||||
Sqliteconfig_Init(interp);
|
||||
Sqlitetest1_Init(interp);
|
||||
@@ -3595,6 +3614,10 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestintarray_Init(interp);
|
||||
Sqlitetestvfs_Init(interp);
|
||||
SqlitetestStat_Init(interp);
|
||||
Sqlitetestrtree_Init(interp);
|
||||
Sqlitequota_Init(interp);
|
||||
Sqlitemultiplex_Init(interp);
|
||||
SqliteSuperlock_Init(interp);
|
||||
|
||||
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
|
||||
|
||||
|
||||
+400
-40
@@ -1235,7 +1235,7 @@ static int sqlite3_mprintf_int64(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(i=2; i<5; i++){
|
||||
if( !sqlite3Atoi64(argv[i], &a[i-2]) ){
|
||||
if( sqlite3Atoi64(argv[i], &a[i-2], 1000000, SQLITE_UTF8) ){
|
||||
Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -1590,6 +1590,81 @@ static int test_table_column_metadata(
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
|
||||
static int blobHandleFromObj(
|
||||
Tcl_Interp *interp,
|
||||
Tcl_Obj *pObj,
|
||||
sqlite3_blob **ppBlob
|
||||
){
|
||||
char *z;
|
||||
int n;
|
||||
|
||||
z = Tcl_GetStringFromObj(pObj, &n);
|
||||
if( n==0 ){
|
||||
*ppBlob = 0;
|
||||
}else{
|
||||
int notUsed;
|
||||
Tcl_Channel channel;
|
||||
ClientData instanceData;
|
||||
|
||||
channel = Tcl_GetChannel(interp, z, ¬Used);
|
||||
if( !channel ) return TCL_ERROR;
|
||||
|
||||
Tcl_Flush(channel);
|
||||
Tcl_Seek(channel, 0, SEEK_SET);
|
||||
|
||||
instanceData = Tcl_GetChannelInstanceData(channel);
|
||||
*ppBlob = *((sqlite3_blob **)instanceData);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_bytes CHANNEL
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
nByte = sqlite3_blob_bytes(pBlob);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_close CHANNEL
|
||||
*/
|
||||
static int test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
sqlite3_blob_close(pBlob);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_read CHANNEL OFFSET N
|
||||
**
|
||||
@@ -1611,13 +1686,10 @@ static int test_blob_read(
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
Tcl_Channel channel;
|
||||
ClientData instanceData;
|
||||
sqlite3_blob *pBlob;
|
||||
int notUsed;
|
||||
int nByte;
|
||||
int iOffset;
|
||||
unsigned char *zBuf;
|
||||
unsigned char *zBuf = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
@@ -1625,19 +1697,16 @@ static int test_blob_read(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), ¬Used);
|
||||
if( !channel
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
|
||||
|| nByte<0 || iOffset<0
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
instanceData = Tcl_GetChannelInstanceData(channel);
|
||||
pBlob = *((sqlite3_blob **)instanceData);
|
||||
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
if( nByte>0 ){
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
|
||||
@@ -1669,10 +1738,7 @@ static int test_blob_write(
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
Tcl_Channel channel;
|
||||
ClientData instanceData;
|
||||
sqlite3_blob *pBlob;
|
||||
int notUsed;
|
||||
int iOffset;
|
||||
int rc;
|
||||
|
||||
@@ -1684,14 +1750,11 @@ static int test_blob_write(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), ¬Used);
|
||||
if( !channel || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
instanceData = Tcl_GetChannelInstanceData(channel);
|
||||
pBlob = *((sqlite3_blob **)instanceData);
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
@@ -1703,6 +1766,33 @@ static int test_blob_write(
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
static int test_blob_reopen(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
Tcl_WideInt iRowid;
|
||||
sqlite3_blob *pBlob;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL ROWID");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[2], &iRowid) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_blob_reopen(pBlob, iRowid);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
|
||||
}
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1790,6 +1880,129 @@ static int test_create_collation_v2(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** USAGE: sqlite3_create_function_v2 DB NAME NARG ENC ?SWITCHES?
|
||||
**
|
||||
** Available switches are:
|
||||
**
|
||||
** -func SCRIPT
|
||||
** -step SCRIPT
|
||||
** -final SCRIPT
|
||||
** -destroy SCRIPT
|
||||
*/
|
||||
typedef struct CreateFunctionV2 CreateFunctionV2;
|
||||
struct CreateFunctionV2 {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pFunc; /* Script for function invocation */
|
||||
Tcl_Obj *pStep; /* Script for agg. step invocation */
|
||||
Tcl_Obj *pFinal; /* Script for agg. finalization invocation */
|
||||
Tcl_Obj *pDestroy; /* Destructor script */
|
||||
};
|
||||
static void cf2Func(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
||||
}
|
||||
static void cf2Step(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
||||
}
|
||||
static void cf2Final(sqlite3_context *ctx){
|
||||
}
|
||||
static void cf2Destroy(void *pUser){
|
||||
CreateFunctionV2 *p = (CreateFunctionV2 *)pUser;
|
||||
|
||||
if( p->interp && p->pDestroy ){
|
||||
int rc = Tcl_EvalObjEx(p->interp, p->pDestroy, 0);
|
||||
if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
|
||||
}
|
||||
|
||||
if( p->pFunc ) Tcl_DecrRefCount(p->pFunc);
|
||||
if( p->pStep ) Tcl_DecrRefCount(p->pStep);
|
||||
if( p->pFinal ) Tcl_DecrRefCount(p->pFinal);
|
||||
if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
static int test_create_function_v2(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The invoking TCL interpreter */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zFunc;
|
||||
int nArg;
|
||||
int enc;
|
||||
CreateFunctionV2 *p;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
struct EncTable {
|
||||
const char *zEnc;
|
||||
int enc;
|
||||
} aEnc[] = {
|
||||
{"utf8", SQLITE_UTF8 },
|
||||
{"utf16", SQLITE_UTF16 },
|
||||
{"utf16le", SQLITE_UTF16LE },
|
||||
{"utf16be", SQLITE_UTF16BE },
|
||||
{"any", SQLITE_ANY },
|
||||
{"0", 0 }
|
||||
};
|
||||
|
||||
if( objc<5 || (objc%2)==0 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG ENC SWITCHES...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zFunc = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &nArg) ) return TCL_ERROR;
|
||||
if( Tcl_GetIndexFromObjStruct(interp, objv[4], aEnc, sizeof(aEnc[0]),
|
||||
"encoding", 0, &enc)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
enc = aEnc[enc].enc;
|
||||
|
||||
p = sqlite3_malloc(sizeof(CreateFunctionV2));
|
||||
assert( p );
|
||||
memset(p, 0, sizeof(CreateFunctionV2));
|
||||
p->interp = interp;
|
||||
|
||||
for(i=5; i<objc; i+=2){
|
||||
int iSwitch;
|
||||
const char *azSwitch[] = {"-func", "-step", "-final", "-destroy", 0};
|
||||
if( Tcl_GetIndexFromObj(interp, objv[i], azSwitch, "switch", 0, &iSwitch) ){
|
||||
sqlite3_free(p);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( iSwitch ){
|
||||
case 0: p->pFunc = objv[i+1]; break;
|
||||
case 1: p->pStep = objv[i+1]; break;
|
||||
case 2: p->pFinal = objv[i+1]; break;
|
||||
case 3: p->pDestroy = objv[i+1]; break;
|
||||
}
|
||||
}
|
||||
if( p->pFunc ) p->pFunc = Tcl_DuplicateObj(p->pFunc);
|
||||
if( p->pStep ) p->pStep = Tcl_DuplicateObj(p->pStep);
|
||||
if( p->pFinal ) p->pFinal = Tcl_DuplicateObj(p->pFinal);
|
||||
if( p->pDestroy ) p->pDestroy = Tcl_DuplicateObj(p->pDestroy);
|
||||
|
||||
if( p->pFunc ) Tcl_IncrRefCount(p->pFunc);
|
||||
if( p->pStep ) Tcl_IncrRefCount(p->pStep);
|
||||
if( p->pFinal ) Tcl_IncrRefCount(p->pFinal);
|
||||
if( p->pDestroy ) Tcl_IncrRefCount(p->pDestroy);
|
||||
|
||||
rc = sqlite3_create_function_v2(db, zFunc, nArg, enc, (void *)p,
|
||||
(p->pFunc ? cf2Func : 0),
|
||||
(p->pStep ? cf2Step : 0),
|
||||
(p->pFinal ? cf2Final : 0),
|
||||
cf2Destroy
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
|
||||
*/
|
||||
@@ -2086,6 +2299,33 @@ static int test_next_stmt(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_readonly STMT
|
||||
**
|
||||
** Return true if STMT is a NULL pointer or a pointer to a statement
|
||||
** that is guaranteed to leave the database unmodified.
|
||||
*/
|
||||
static int test_stmt_readonly(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
rc = sqlite3_stmt_readonly(pStmt);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_reset STMT
|
||||
@@ -2291,7 +2531,7 @@ static int test_collate_func(
|
||||
int nB, const void *zB
|
||||
){
|
||||
Tcl_Interp *i = pTestCollateInterp;
|
||||
int encin = (int)pCtx;
|
||||
int encin = SQLITE_PTR_TO_INT(pCtx);
|
||||
int res;
|
||||
int n;
|
||||
|
||||
@@ -2420,7 +2660,7 @@ static void test_collate_needed_cb(
|
||||
}
|
||||
zNeededCollation[i] = 0;
|
||||
sqlite3_create_collation(
|
||||
db, "test_collate", ENC(db), (void *)enc, test_collate_func);
|
||||
db, "test_collate", ENC(db), SQLITE_INT_TO_PTR(enc), test_collate_func);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2469,8 +2709,8 @@ static int alignmentCollFunc(
|
||||
){
|
||||
int rc, n;
|
||||
n = nKey1<nKey2 ? nKey1 : nKey2;
|
||||
if( nKey1>0 && 1==(1&(int)pKey1) ) unaligned_string_counter++;
|
||||
if( nKey2>0 && 1==(1&(int)pKey2) ) unaligned_string_counter++;
|
||||
if( nKey1>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey1))) ) unaligned_string_counter++;
|
||||
if( nKey2>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey2))) ) unaligned_string_counter++;
|
||||
rc = memcmp(pKey1, pKey2, n);
|
||||
if( rc==0 ){
|
||||
rc = nKey1 - nKey2;
|
||||
@@ -4266,20 +4506,17 @@ static int test_soft_heap_limit(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
static int softHeapLimit = 0;
|
||||
int amt;
|
||||
sqlite3_int64 amt;
|
||||
sqlite3_int64 N = -1;
|
||||
if( objc!=1 && objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?N?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
amt = softHeapLimit;
|
||||
if( objc==2 ){
|
||||
int N;
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
|
||||
sqlite3_soft_heap_limit(N);
|
||||
softHeapLimit = N;
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(amt));
|
||||
amt = sqlite3_soft_heap_limit64(N);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(amt));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -4564,13 +4801,13 @@ static int file_control_test(
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_file_control(db, 0, 0, &iArg);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
assert( rc==SQLITE_NOTFOUND );
|
||||
rc = sqlite3_file_control(db, "notadatabase", SQLITE_FCNTL_LOCKSTATE, &iArg);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
rc = sqlite3_file_control(db, "main", -1, &iArg);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
assert( rc==SQLITE_NOTFOUND );
|
||||
rc = sqlite3_file_control(db, "temp", -1, &iArg);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
assert( rc==SQLITE_NOTFOUND || rc==SQLITE_ERROR );
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -5021,6 +5258,122 @@ static int runAsObjProc(
|
||||
return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/*
|
||||
** WARNING: The following function, printExplainQueryPlan() is an exact
|
||||
** copy of example code from eqp.in (eqp.html). If this code is modified,
|
||||
** then the documentation copy needs to be modified as well.
|
||||
*/
|
||||
/*
|
||||
** Argument pStmt is a prepared SQL statement. This function compiles
|
||||
** an EXPLAIN QUERY PLAN command to report on the prepared statement,
|
||||
** and prints the report to stdout using printf().
|
||||
*/
|
||||
int printExplainQueryPlan(sqlite3_stmt *pStmt){
|
||||
const char *zSql; /* Input SQL */
|
||||
char *zExplain; /* SQL with EXPLAIN QUERY PLAN prepended */
|
||||
sqlite3_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */
|
||||
int rc; /* Return code from sqlite3_prepare_v2() */
|
||||
|
||||
zSql = sqlite3_sql(pStmt);
|
||||
if( zSql==0 ) return SQLITE_ERROR;
|
||||
|
||||
zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql);
|
||||
if( zExplain==0 ) return SQLITE_NOMEM;
|
||||
|
||||
rc = sqlite3_prepare_v2(sqlite3_db_handle(pStmt), zExplain, -1, &pExplain, 0);
|
||||
sqlite3_free(zExplain);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
while( SQLITE_ROW==sqlite3_step(pExplain) ){
|
||||
int iSelectid = sqlite3_column_int(pExplain, 0);
|
||||
int iOrder = sqlite3_column_int(pExplain, 1);
|
||||
int iFrom = sqlite3_column_int(pExplain, 2);
|
||||
const char *zDetail = (const char *)sqlite3_column_text(pExplain, 3);
|
||||
|
||||
printf("%d %d %d %s\n", iSelectid, iOrder, iFrom, zDetail);
|
||||
}
|
||||
|
||||
return sqlite3_finalize(pExplain);
|
||||
}
|
||||
|
||||
static int test_print_eqp(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
rc = printExplainQueryPlan(pStmt);
|
||||
Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
/*
|
||||
** optimization_control DB OPT BOOLEAN
|
||||
**
|
||||
** Enable or disable query optimizations using the sqlite3_test_control()
|
||||
** interface. Disable if BOOLEAN is false and enable if BOOLEAN is true.
|
||||
** OPT is the name of the optimization to be disabled.
|
||||
*/
|
||||
static int optimization_control(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int i;
|
||||
sqlite3 *db;
|
||||
const char *zOpt;
|
||||
int onoff;
|
||||
int mask;
|
||||
static const struct {
|
||||
const char *zOptName;
|
||||
int mask;
|
||||
} aOpt[] = {
|
||||
{ "all", SQLITE_OptMask },
|
||||
{ "query-flattener", SQLITE_QueryFlattener },
|
||||
{ "column-cache", SQLITE_ColumnCache },
|
||||
{ "index-sort", SQLITE_IndexSort },
|
||||
{ "index-search", SQLITE_IndexSearch },
|
||||
{ "index-cover", SQLITE_IndexCover },
|
||||
{ "groupby-order", SQLITE_GroupByOrder },
|
||||
{ "factor-constants", SQLITE_FactorOutConst },
|
||||
};
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB OPT BOOLEAN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[3], &onoff) ) return TCL_ERROR;
|
||||
zOpt = Tcl_GetString(objv[2]);
|
||||
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
|
||||
if( strcmp(zOpt, aOpt[i].zOptName)==0 ){
|
||||
mask = aOpt[i].mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( onoff ) mask = ~mask;
|
||||
if( i>=sizeof(aOpt)/sizeof(aOpt[0]) ){
|
||||
Tcl_AppendResult(interp, "unknown optimization - should be one of:",
|
||||
(char*)0);
|
||||
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
|
||||
Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
|
||||
}
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, mask);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
@@ -5124,6 +5477,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_step", test_step ,0 },
|
||||
{ "sqlite3_sql", test_sql ,0 },
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
|
||||
|
||||
{ "sqlite3_release_memory", test_release_memory, 0},
|
||||
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
|
||||
@@ -5138,6 +5492,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "save_prng_state", save_prng_state, 0 },
|
||||
{ "restore_prng_state", restore_prng_state, 0 },
|
||||
{ "reset_prng_state", reset_prng_state, 0 },
|
||||
{ "optimization_control", optimization_control,0},
|
||||
{ "tcl_objproc", runAsObjProc, 0 },
|
||||
|
||||
/* sqlite3_column_*() API */
|
||||
@@ -5187,6 +5542,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
|
||||
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
|
||||
{ "sqlite3_vfs_list", vfs_list, 0 },
|
||||
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
|
||||
|
||||
/* Functions from os.h */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@@ -5205,15 +5561,19 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
{ "sqlite3_blob_read", test_blob_read, 0 },
|
||||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
{ "sqlite3_blob_read", test_blob_read, 0 },
|
||||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
{ "sqlite3_blob_reopen", test_blob_reopen, 0 },
|
||||
{ "sqlite3_blob_bytes", test_blob_bytes, 0 },
|
||||
{ "sqlite3_blob_close", test_blob_close, 0 },
|
||||
#endif
|
||||
{ "pcache_stats", test_pcache_stats, 0 },
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
{ "sqlite3_unlock_notify", test_unlock_notify, 0 },
|
||||
#endif
|
||||
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
|
||||
{ "test_sqlite3_log", test_sqlite3_log, 0 },
|
||||
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
|
||||
{ "test_sqlite3_log", test_sqlite3_log, 0 },
|
||||
{ "print_explain_query_plan", test_print_eqp, 0 },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
|
||||
+4
-5
@@ -53,7 +53,7 @@ static sqlite3 sDb;
|
||||
static int nRefSqlite3 = 0;
|
||||
|
||||
/*
|
||||
** Usage: btree_open FILENAME NCACHE FLAGS
|
||||
** Usage: btree_open FILENAME NCACHE
|
||||
**
|
||||
** Open a new database
|
||||
*/
|
||||
@@ -64,22 +64,21 @@ static int btree_open(
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Btree *pBt;
|
||||
int rc, nCache, flags;
|
||||
int rc, nCache;
|
||||
char zBuf[100];
|
||||
if( argc!=4 ){
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" FILENAME NCACHE FLAGS\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
|
||||
if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR;
|
||||
nRefSqlite3++;
|
||||
if( nRefSqlite3==1 ){
|
||||
sDb.pVfs = sqlite3_vfs_find(0);
|
||||
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
sqlite3_mutex_enter(sDb.mutex);
|
||||
}
|
||||
rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags,
|
||||
rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
||||
@@ -133,6 +133,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "autoindex", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_AUTORESET
|
||||
Tcl_SetVar2(interp, "sqlite_options", "autoreset", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "autoreset", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -523,6 +529,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
||||
+9
-4
@@ -1290,10 +1290,13 @@ static int test_db_status(
|
||||
const char *zName;
|
||||
int op;
|
||||
} aOp[] = {
|
||||
{ "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED },
|
||||
{ "SQLITE_DBSTATUS_CACHE_USED", SQLITE_DBSTATUS_CACHE_USED },
|
||||
{ "SQLITE_DBSTATUS_SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED },
|
||||
{ "SQLITE_DBSTATUS_STMT_USED", SQLITE_DBSTATUS_STMT_USED }
|
||||
{ "LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED },
|
||||
{ "CACHE_USED", SQLITE_DBSTATUS_CACHE_USED },
|
||||
{ "SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED },
|
||||
{ "STMT_USED", SQLITE_DBSTATUS_STMT_USED },
|
||||
{ "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT },
|
||||
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
@@ -1302,6 +1305,8 @@ static int test_db_status(
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zOpName = Tcl_GetString(objv[2]);
|
||||
if( memcmp(zOpName, "SQLITE_", 7)==0 ) zOpName += 7;
|
||||
if( memcmp(zOpName, "DBSTATUS_", 9)==0 ) zOpName += 9;
|
||||
for(i=0; i<ArraySize(aOp); i++){
|
||||
if( strcmp(aOp[i].zName, zOpName)==0 ){
|
||||
op = aOp[i].op;
|
||||
|
||||
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
** 2010 October 28
|
||||
**
|
||||
** 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 a VFS "shim" - a layer that sits in between the
|
||||
** pager and the real VFS.
|
||||
**
|
||||
** This particular shim enforces a multiplex system on DB files.
|
||||
** This shim shards/partitions a single DB file into smaller
|
||||
** "chunks" such that the total DB file size may exceed the maximum
|
||||
** file size of the underlying file system.
|
||||
**
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/************************ Shim Definitions ******************************/
|
||||
|
||||
/* This is the limit on the chunk size. It may be changed by calling
|
||||
** the sqlite3_multiplex_set() interface.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
|
||||
/* Default limit on number of chunks. Care should be taken
|
||||
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
|
||||
** format specifier. It may be changed by calling
|
||||
** the sqlite3_multiplex_set() interface.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
|
||||
|
||||
/* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the
|
||||
** last SQLITE_MULTIPLEX_EXT_SZ characters of the
|
||||
** filename will be overwritten, otherwise, the
|
||||
** multiplex extension is simply appended to the filename.
|
||||
** Ex. (undefined) test.db -> test.db01
|
||||
** (defined) test.db -> test.01
|
||||
** Chunk 0 does not have a modified extension.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_EXT_FMT "%02d"
|
||||
#define SQLITE_MULTIPLEX_EXT_SZ 2
|
||||
|
||||
/************************ Object Definitions ******************************/
|
||||
|
||||
/* Forward declaration of all object types */
|
||||
typedef struct multiplexGroup multiplexGroup;
|
||||
typedef struct multiplexConn multiplexConn;
|
||||
|
||||
/*
|
||||
** A "multiplex group" is a collection of files that collectively
|
||||
** makeup a single SQLite DB file. This allows the size of the DB
|
||||
** to exceed the limits imposed by the file system.
|
||||
**
|
||||
** There is an instance of the following object for each defined multiplex
|
||||
** group.
|
||||
*/
|
||||
struct multiplexGroup {
|
||||
sqlite3_file **pReal; /* Handles to each chunk */
|
||||
char *bOpen; /* 0 if chunk not opened */
|
||||
char *zName; /* Base filename of this group */
|
||||
int nName; /* Length of base filename */
|
||||
int flags; /* Flags used for original opening */
|
||||
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following object represents each open connection
|
||||
** to a file that is multiplex'ed. This object is a
|
||||
** subclass of sqlite3_file. The sqlite3_file object for the underlying
|
||||
** VFS is appended to this structure.
|
||||
*/
|
||||
struct multiplexConn {
|
||||
sqlite3_file base; /* Base class - must be first */
|
||||
multiplexGroup *pGroup; /* The underlying group of files */
|
||||
};
|
||||
|
||||
/************************* Global Variables **********************************/
|
||||
/*
|
||||
** All global variables used by this file are containing within the following
|
||||
** gMultiplex structure.
|
||||
*/
|
||||
static struct {
|
||||
/* The pOrigVfs is the real, original underlying VFS implementation.
|
||||
** Most operations pass-through to the real VFS. This value is read-only
|
||||
** during operation. It is only modified at start-time and thus does not
|
||||
** require a mutex.
|
||||
*/
|
||||
sqlite3_vfs *pOrigVfs;
|
||||
|
||||
/* The sThisVfs is the VFS structure used by this shim. It is initialized
|
||||
** at start-time and thus does not require a mutex
|
||||
*/
|
||||
sqlite3_vfs sThisVfs;
|
||||
|
||||
/* 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
|
||||
** 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
|
||||
** for version 2.
|
||||
*/
|
||||
sqlite3_io_methods sIoMethodsV1;
|
||||
sqlite3_io_methods sIoMethodsV2;
|
||||
|
||||
/* True when this shim has been initialized.
|
||||
*/
|
||||
int isInitialized;
|
||||
|
||||
/* For run-time access any of the other global data structures in this
|
||||
** shim, the following mutex must be held.
|
||||
*/
|
||||
sqlite3_mutex *pMutex;
|
||||
|
||||
/* List of multiplexGroup objects.
|
||||
*/
|
||||
multiplexGroup *pGroups;
|
||||
|
||||
/* Chunk params.
|
||||
*/
|
||||
int nChunkSize;
|
||||
int nMaxChunks;
|
||||
|
||||
/* Storage for temp file names. Allocated during
|
||||
** initialization to the max pathname of the underlying VFS.
|
||||
*/
|
||||
char *zName;
|
||||
|
||||
} gMultiplex;
|
||||
|
||||
/************************* Utility Routines *********************************/
|
||||
/*
|
||||
** Acquire and release the mutex used to serialize access to the
|
||||
** list of multiplexGroups.
|
||||
*/
|
||||
static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
|
||||
static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
|
||||
|
||||
/* Translate an sqlite3_file* that is really a multiplexGroup* into
|
||||
** the sqlite3_file* for the underlying original VFS.
|
||||
*/
|
||||
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
|
||||
multiplexGroup *pGroup = pConn->pGroup;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
if( iChunk<gMultiplex.nMaxChunks ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
|
||||
if( !pGroup->bOpen[iChunk] ){
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
if( iChunk ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
|
||||
#endif
|
||||
}
|
||||
*rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags);
|
||||
if( *rc==SQLITE_OK ){
|
||||
pGroup->bOpen[iChunk] = -1;
|
||||
return pSubOpen;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
*rc = SQLITE_OK;
|
||||
return pSubOpen;
|
||||
}
|
||||
*rc = SQLITE_FULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
|
||||
/*
|
||||
** This is the xOpen method used for the "multiplex" VFS.
|
||||
**
|
||||
** Most of the work is done by the underlying original VFS. This method
|
||||
** simply links the new file into the appropriate multiplex group if it is a
|
||||
** file that needs to be tracked.
|
||||
*/
|
||||
static int multiplexOpen(
|
||||
sqlite3_vfs *pVfs, /* The multiplex VFS */
|
||||
const char *zName, /* Name of file to be opened */
|
||||
sqlite3_file *pConn, /* Fill in this file descriptor */
|
||||
int flags, /* Flags to control the opening */
|
||||
int *pOutFlags /* Flags showing results of opening */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
|
||||
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
|
||||
sqlite3_file *pSubOpen; /* Real file descriptor */
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int i;
|
||||
int sz;
|
||||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
/* We need to create a group structure and manage
|
||||
** access to this group of files.
|
||||
*/
|
||||
multiplexEnter();
|
||||
pMultiplexOpen = (multiplexConn*)pConn;
|
||||
/* allocate space for group */
|
||||
sz = sizeof(multiplexGroup) /* multiplexGroup */
|
||||
+ (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
|
||||
+ (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */
|
||||
+ gMultiplex.nMaxChunks /* bOpen[] */
|
||||
+ nName + 1; /* zName */
|
||||
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sz += SQLITE_MULTIPLEX_EXT_SZ;
|
||||
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
|
||||
#else
|
||||
assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
|
||||
assert(nName < pOrigVfs->mxPathname);
|
||||
#endif
|
||||
pGroup = sqlite3_malloc( sz );
|
||||
if( pGroup==0 ){
|
||||
rc=SQLITE_NOMEM;
|
||||
}else{
|
||||
/* assign pointers to extra space allocated */
|
||||
char *p = (char *)&pGroup[1];
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
memset(pGroup, 0, sz);
|
||||
pGroup->pReal = (sqlite3_file **)p;
|
||||
p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
pGroup->pReal[i] = (sqlite3_file *)p;
|
||||
p += pOrigVfs->szOsFile;
|
||||
}
|
||||
pGroup->bOpen = p;
|
||||
p += gMultiplex.nMaxChunks;
|
||||
pGroup->zName = p;
|
||||
/* save off base filename, name length, and original open flags */
|
||||
memcpy(pGroup->zName, zName, nName+1);
|
||||
pGroup->nName = nName;
|
||||
pGroup->flags = flags;
|
||||
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
|
||||
if( pSubOpen ){
|
||||
if( pSubOpen->pMethods->iVersion==1 ){
|
||||
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
|
||||
}else{
|
||||
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
|
||||
}
|
||||
/* place this group at the head of our list */
|
||||
pGroup->pNext = gMultiplex.pGroups;
|
||||
if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
|
||||
gMultiplex.pGroups = pGroup;
|
||||
}else{
|
||||
sqlite3_free(pGroup);
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xDelete method used for the "multiplex" VFS.
|
||||
** It attempts to delete the filename specified, as well
|
||||
** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension.
|
||||
*/
|
||||
static int multiplexDelete(
|
||||
sqlite3_vfs *pVfs, /* The multiplex VFS */
|
||||
const char *zName, /* Name of file to delete */
|
||||
int syncDir
|
||||
){
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int rc = SQLITE_OK;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int i;
|
||||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
multiplexEnter();
|
||||
memcpy(gMultiplex.zName, zName, nName+1);
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
int rc2;
|
||||
int exists = 0;
|
||||
if( i ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
}
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
/* if it exists, delete it */
|
||||
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
|
||||
return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
|
||||
}
|
||||
static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
|
||||
return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
|
||||
}
|
||||
static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
|
||||
return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
|
||||
}
|
||||
static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
|
||||
gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
|
||||
}
|
||||
static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
|
||||
return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
|
||||
}
|
||||
static void multiplexDlClose(sqlite3_vfs *a, void *b){
|
||||
gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
|
||||
}
|
||||
static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
|
||||
return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
|
||||
}
|
||||
static int multiplexSleep(sqlite3_vfs *a, int b){
|
||||
return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
|
||||
}
|
||||
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);
|
||||
}
|
||||
static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
|
||||
return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
|
||||
}
|
||||
|
||||
/************************ I/O Method Wrappers *******************************/
|
||||
|
||||
/* xClose requests get passed through to the original VFS.
|
||||
** We loop over all open chunk handles and close them.
|
||||
** The group structure for this file is unlinked from
|
||||
** our list of groups and freed.
|
||||
*/
|
||||
static int multiplexClose(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
/* close any open handles */
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
if( pGroup->bOpen[i] ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[i];
|
||||
int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
pGroup->bOpen[i] = 0;
|
||||
}
|
||||
}
|
||||
/* remove from linked list */
|
||||
if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
|
||||
if( pGroup->pPrev ){
|
||||
pGroup->pPrev->pNext = pGroup->pNext;
|
||||
}else{
|
||||
gMultiplex.pGroups = pGroup->pNext;
|
||||
}
|
||||
sqlite3_free(pGroup);
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xRead requests thru to the original VFS after
|
||||
** determining the correct chunk to operate on.
|
||||
** Break up reads across chunk boundaries.
|
||||
*/
|
||||
static int multiplexRead(
|
||||
sqlite3_file *pConn,
|
||||
void *pBuf,
|
||||
int iAmt,
|
||||
sqlite3_int64 iOfst
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst/gMultiplex.nChunkSize);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
|
||||
if( extra<0 ) extra = 0;
|
||||
iAmt -= extra;
|
||||
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pBuf = (char *)pBuf + iAmt;
|
||||
iOfst += iAmt;
|
||||
iAmt = extra;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_READ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xWrite requests thru to the original VFS after
|
||||
** determining the correct chunk to operate on.
|
||||
** Break up writes across chunk boundaries.
|
||||
*/
|
||||
static int multiplexWrite(
|
||||
sqlite3_file *pConn,
|
||||
const void *pBuf,
|
||||
int iAmt,
|
||||
sqlite3_int64 iOfst
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst/gMultiplex.nChunkSize);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
|
||||
if( extra<0 ) extra = 0;
|
||||
iAmt -= extra;
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pBuf = (char *)pBuf + iAmt;
|
||||
iOfst += iAmt;
|
||||
iAmt = extra;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_WRITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xTruncate requests thru to the original VFS after
|
||||
** determining the correct chunk to operate on. Delete any
|
||||
** chunks above the truncate mark.
|
||||
*/
|
||||
static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int rc2;
|
||||
int i;
|
||||
sqlite3_file *pSubOpen;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
multiplexEnter();
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
/* delete the chunks above the truncate limit */
|
||||
for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
|
||||
/* close any open chunks before deleting them */
|
||||
if( pGroup->bOpen[i] ){
|
||||
pSubOpen = pGroup->pReal[i];
|
||||
rc2 = pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
|
||||
pGroup->bOpen[i] = 0;
|
||||
}
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
|
||||
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
|
||||
if( pSubOpen ){
|
||||
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xSync requests through to the original VFS without change
|
||||
*/
|
||||
static int multiplexSync(sqlite3_file *pConn, int flags){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
/* if we don't have it open, we don't need to sync it */
|
||||
if( pGroup->bOpen[i] ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[i];
|
||||
int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xFileSize requests through to the original VFS.
|
||||
** Aggregate the size of all the chunks before returning.
|
||||
*/
|
||||
static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int rc2;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
*pSize = 0;
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
sqlite3_file *pSubOpen = NULL;
|
||||
/* if not opened already, check to see if the chunk exists */
|
||||
if( pGroup->bOpen[i] ){
|
||||
pSubOpen = pGroup->pReal[i];
|
||||
}else{
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int exists = 0;
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
if( i ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
}
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
/* if it exists, open it */
|
||||
pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
}else{
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pSubOpen ){
|
||||
sqlite3_int64 sz;
|
||||
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc2!=SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}else{
|
||||
if( sz>gMultiplex.nChunkSize ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
*pSize += sz;
|
||||
}
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexLock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xLock(pSubOpen, lock);
|
||||
}
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
|
||||
/* Pass xUnlock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexUnlock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
|
||||
}
|
||||
return SQLITE_IOERR_UNLOCK;
|
||||
}
|
||||
|
||||
/* Pass xCheckReservedLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
|
||||
}
|
||||
return SQLITE_IOERR_CHECKRESERVEDLOCK;
|
||||
}
|
||||
|
||||
/* Pass xFileControl requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen;
|
||||
if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
|
||||
pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Pass xSectorSize requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexSectorSize(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xSectorSize(pSubOpen);
|
||||
}
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass xShmMap requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexShmMap(
|
||||
sqlite3_file *pConn, /* Handle open on database file */
|
||||
int iRegion, /* Region to retrieve */
|
||||
int szRegion, /* Size of regions */
|
||||
int bExtend, /* True to extend file if necessary */
|
||||
void volatile **pp /* OUT: Mapped memory */
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
|
||||
}
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
|
||||
/* Pass xShmLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexShmLock(
|
||||
sqlite3_file *pConn, /* Database file holding the shared memory */
|
||||
int ofst, /* First lock to acquire or release */
|
||||
int n, /* Number of locks to acquire or release */
|
||||
int flags /* What to do with the lock */
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
|
||||
}
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
|
||||
/* Pass xShmBarrier requests through to the original VFS unchanged.
|
||||
*/
|
||||
static void multiplexShmBarrier(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
pSubOpen->pMethods->xShmBarrier(pSubOpen);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass xShmUnmap requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/************************** Public Interfaces *****************************/
|
||||
/*
|
||||
** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The multiplex VFS shim is named "multiplex". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
sqlite3_vfs *pOrigVfs;
|
||||
if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
|
||||
pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
|
||||
if( pOrigVfs==0 ) return SQLITE_ERROR;
|
||||
assert( pOrigVfs!=&gMultiplex.sThisVfs );
|
||||
gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
if( !gMultiplex.pMutex ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname);
|
||||
if( !gMultiplex.zName ){
|
||||
sqlite3_mutex_free(gMultiplex.pMutex);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
|
||||
gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
|
||||
gMultiplex.pGroups = NULL;
|
||||
gMultiplex.isInitialized = 1;
|
||||
gMultiplex.pOrigVfs = pOrigVfs;
|
||||
gMultiplex.sThisVfs = *pOrigVfs;
|
||||
gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
|
||||
gMultiplex.sThisVfs.zName = "multiplex";
|
||||
gMultiplex.sThisVfs.xOpen = multiplexOpen;
|
||||
gMultiplex.sThisVfs.xDelete = multiplexDelete;
|
||||
gMultiplex.sThisVfs.xAccess = multiplexAccess;
|
||||
gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
|
||||
gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
|
||||
gMultiplex.sThisVfs.xDlError = multiplexDlError;
|
||||
gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
|
||||
gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
|
||||
gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
|
||||
gMultiplex.sThisVfs.xSleep = multiplexSleep;
|
||||
gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
|
||||
gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
|
||||
gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
|
||||
|
||||
gMultiplex.sIoMethodsV1.iVersion = 1;
|
||||
gMultiplex.sIoMethodsV1.xClose = multiplexClose;
|
||||
gMultiplex.sIoMethodsV1.xRead = multiplexRead;
|
||||
gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
|
||||
gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
|
||||
gMultiplex.sIoMethodsV1.xSync = multiplexSync;
|
||||
gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
|
||||
gMultiplex.sIoMethodsV1.xLock = multiplexLock;
|
||||
gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
|
||||
gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
|
||||
gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
|
||||
gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
|
||||
gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
|
||||
gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
|
||||
gMultiplex.sIoMethodsV2.iVersion = 2;
|
||||
gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
|
||||
gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
|
||||
gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
|
||||
gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
|
||||
sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Shutdown the multiplex system.
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining multiplex groups.
|
||||
*/
|
||||
int sqlite3_multiplex_shutdown(void){
|
||||
if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
|
||||
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
|
||||
gMultiplex.isInitialized = 0;
|
||||
sqlite3_free(gMultiplex.zName);
|
||||
sqlite3_mutex_free(gMultiplex.pMutex);
|
||||
sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
|
||||
memset(&gMultiplex, 0, sizeof(gMultiplex));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Adjust chunking params. VFS should be initialized first.
|
||||
** No files should be open. Re-intializing will reset these
|
||||
** to the default.
|
||||
*/
|
||||
int sqlite3_multiplex_set(
|
||||
int nChunkSize, /* Max chunk size */
|
||||
int nMaxChunks /* Max number of chunks */
|
||||
){
|
||||
if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
|
||||
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
|
||||
if( nChunkSize<32 ) return SQLITE_MISUSE;
|
||||
if( nMaxChunks<1 ) return SQLITE_MISUSE;
|
||||
if( nMaxChunks>99 ) return SQLITE_MISUSE;
|
||||
multiplexEnter();
|
||||
gMultiplex.nChunkSize = nChunkSize;
|
||||
gMultiplex.nMaxChunks = nMaxChunks;
|
||||
multiplexLeave();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
|
||||
extern const char *sqlite3TestErrorName(int);
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
|
||||
*/
|
||||
static int test_multiplex_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zName; /* Name of new multiplex VFS */
|
||||
int makeDefault; /* True to make the new VFS the default */
|
||||
int rc; /* Value returned by multiplex_initialize() */
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zName = Tcl_GetString(objv[1]);
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
|
||||
if( zName[0]=='\0' ) zName = 0;
|
||||
|
||||
/* Call sqlite3_multiplex_initialize() */
|
||||
rc = sqlite3_multiplex_initialize(zName, makeDefault);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_shutdown
|
||||
*/
|
||||
static int test_multiplex_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc; /* Value returned by multiplex_shutdown() */
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Call sqlite3_multiplex_shutdown() */
|
||||
rc = sqlite3_multiplex_shutdown();
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
|
||||
*/
|
||||
static int test_multiplex_set(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int nChunkSize; /* Max chunk size */
|
||||
int nMaxChunks; /* Max number of chunks */
|
||||
int rc; /* Value returned by sqlite3_multiplex_set() */
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
|
||||
|
||||
/* Invoke sqlite3_multiplex_set() */
|
||||
rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_multiplex_dump
|
||||
*/
|
||||
static int test_multiplex_dump(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
Tcl_Obj *pResult;
|
||||
Tcl_Obj *pGroupTerm;
|
||||
multiplexGroup *pGroup;
|
||||
int i;
|
||||
int nChunks = 0;
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
|
||||
pResult = Tcl_NewObj();
|
||||
multiplexEnter();
|
||||
for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
|
||||
pGroupTerm = Tcl_NewObj();
|
||||
|
||||
pGroup->zName[pGroup->nName] = '\0';
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewStringObj(pGroup->zName, -1));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(pGroup->nName));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(pGroup->flags));
|
||||
|
||||
/* count number of chunks with open handles */
|
||||
for(i=0; i<gMultiplex.nMaxChunks; i++){
|
||||
if( pGroup->bOpen[i] ) nChunks++;
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(nChunks));
|
||||
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(gMultiplex.nChunkSize));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(gMultiplex.nMaxChunks));
|
||||
|
||||
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
|
||||
}
|
||||
multiplexLeave();
|
||||
Tcl_SetObjResult(interp, pResult);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine registers the custom TCL commands defined in this
|
||||
** module. This should be the only procedure visible from outside
|
||||
** of this module.
|
||||
*/
|
||||
int Sqlitemultiplex_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_multiplex_initialize", test_multiplex_initialize },
|
||||
{ "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
|
||||
{ "sqlite3_multiplex_set", test_multiplex_set },
|
||||
{ "sqlite3_multiplex_dump", test_multiplex_dump },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -247,7 +247,7 @@ static int test_read_mutex_counters(
|
||||
int ii;
|
||||
char *aName[8] = {
|
||||
"fast", "recursive", "static_master", "static_mem",
|
||||
"static_open", "static_prng", "static_lru", "static_lru2"
|
||||
"static_open", "static_prng", "static_lru", "static_pmem"
|
||||
};
|
||||
|
||||
if( objc!=1 ){
|
||||
|
||||
@@ -0,0 +1,952 @@
|
||||
/*
|
||||
** 2010 September 31
|
||||
**
|
||||
** 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 a VFS "shim" - a layer that sits in between the
|
||||
** pager and the real VFS.
|
||||
**
|
||||
** This particular shim enforces a quota system on files. One or more
|
||||
** database files are in a "quota group" that is defined by a GLOB
|
||||
** pattern. A quota is set for the combined size of all files in the
|
||||
** the group. A quota of zero means "no limit". If the total size
|
||||
** of all files in the quota group is greater than the limit, then
|
||||
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
|
||||
**
|
||||
** However, before returning SQLITE_FULL, the write requests invoke
|
||||
** a callback function that is configurable for each quota group.
|
||||
** This callback has the opportunity to enlarge the quota. If the
|
||||
** callback does enlarge the quota such that the total size of all
|
||||
** files within the group is less than the new quota, then the write
|
||||
** continues as if nothing had happened.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** For an build without mutexes, no-op the mutex calls.
|
||||
*/
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
|
||||
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
|
||||
#define sqlite3_mutex_free(X)
|
||||
#define sqlite3_mutex_enter(X)
|
||||
#define sqlite3_mutex_try(X) SQLITE_OK
|
||||
#define sqlite3_mutex_leave(X)
|
||||
#define sqlite3_mutex_held(X) ((void)(X),1)
|
||||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
#endif /* SQLITE_THREADSAFE==0 */
|
||||
|
||||
|
||||
/************************ Object Definitions ******************************/
|
||||
|
||||
/* Forward declaration of all object types */
|
||||
typedef struct quotaGroup quotaGroup;
|
||||
typedef struct quotaConn quotaConn;
|
||||
typedef struct quotaFile quotaFile;
|
||||
|
||||
/*
|
||||
** A "quota group" is a collection of files whose collective size we want
|
||||
** to limit. Each quota group is defined by a GLOB pattern.
|
||||
**
|
||||
** There is an instance of the following object for each defined quota
|
||||
** group. This object records the GLOB pattern that defines which files
|
||||
** belong to the quota group. The object also remembers the size limit
|
||||
** for the group (the quota) and the callback to be invoked when the
|
||||
** sum of the sizes of the files within the group goes over the limit.
|
||||
**
|
||||
** A quota group must be established (using sqlite3_quota_set(...))
|
||||
** prior to opening any of the database connections that access files
|
||||
** within the quota group.
|
||||
*/
|
||||
struct quotaGroup {
|
||||
const char *zPattern; /* Filename pattern to be quotaed */
|
||||
sqlite3_int64 iLimit; /* Upper bound on total file size */
|
||||
sqlite3_int64 iSize; /* Current size of all files */
|
||||
void (*xCallback)( /* Callback invoked when going over quota */
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
);
|
||||
void *pArg; /* Third argument to the xCallback() */
|
||||
void (*xDestroy)(void*); /* Optional destructor for pArg */
|
||||
quotaGroup *pNext, **ppPrev; /* Doubly linked list of all quota objects */
|
||||
quotaFile *pFiles; /* Files within this group */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure represents a single file that is part
|
||||
** of a quota group. A single file can be opened multiple times. In
|
||||
** order keep multiple openings of the same file from causing the size
|
||||
** of the file to count against the quota multiple times, each file
|
||||
** has a unique instance of this object and multiple open connections
|
||||
** to the same file each point to a single instance of this object.
|
||||
*/
|
||||
struct quotaFile {
|
||||
char *zFilename; /* Name of this file */
|
||||
quotaGroup *pGroup; /* Quota group to which this file belongs */
|
||||
sqlite3_int64 iSize; /* Current size of this file */
|
||||
int nRef; /* Number of times this file is open */
|
||||
quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following object represents each open connection
|
||||
** 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.
|
||||
*/
|
||||
struct quotaConn {
|
||||
sqlite3_file base; /* Base class - must be first */
|
||||
quotaFile *pFile; /* The underlying file */
|
||||
/* The underlying VFS sqlite3_file is appended to this object */
|
||||
};
|
||||
|
||||
/************************* Global Variables **********************************/
|
||||
/*
|
||||
** All global variables used by this file are containing within the following
|
||||
** gQuota structure.
|
||||
*/
|
||||
static struct {
|
||||
/* The pOrigVfs is the real, original underlying VFS implementation.
|
||||
** Most operations pass-through to the real VFS. This value is read-only
|
||||
** during operation. It is only modified at start-time and thus does not
|
||||
** require a mutex.
|
||||
*/
|
||||
sqlite3_vfs *pOrigVfs;
|
||||
|
||||
/* The sThisVfs is the VFS structure used by this shim. It is initialized
|
||||
** at start-time and thus does not require a mutex
|
||||
*/
|
||||
sqlite3_vfs sThisVfs;
|
||||
|
||||
/* 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
|
||||
** 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
|
||||
** for version 2.
|
||||
*/
|
||||
sqlite3_io_methods sIoMethodsV1;
|
||||
sqlite3_io_methods sIoMethodsV2;
|
||||
|
||||
/* True when this shim as been initialized.
|
||||
*/
|
||||
int isInitialized;
|
||||
|
||||
/* For run-time access any of the other global data structures in this
|
||||
** shim, the following mutex must be held.
|
||||
*/
|
||||
sqlite3_mutex *pMutex;
|
||||
|
||||
/* List of quotaGroup objects.
|
||||
*/
|
||||
quotaGroup *pGroup;
|
||||
|
||||
} gQuota;
|
||||
|
||||
/************************* Utility Routines *********************************/
|
||||
/*
|
||||
** Acquire and release the mutex used to serialize access to the
|
||||
** list of quotaGroups.
|
||||
*/
|
||||
static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
|
||||
static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
|
||||
|
||||
|
||||
/* If the reference count and threshold for a quotaGroup are both
|
||||
** zero, then destroy the quotaGroup.
|
||||
*/
|
||||
static void quotaGroupDeref(quotaGroup *pGroup){
|
||||
if( pGroup->pFiles==0 && pGroup->iLimit==0 ){
|
||||
*pGroup->ppPrev = pGroup->pNext;
|
||||
if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
|
||||
if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
|
||||
sqlite3_free(pGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if string z matches glob pattern zGlob.
|
||||
**
|
||||
** Globbing rules:
|
||||
**
|
||||
** '*' Matches any sequence of zero or more characters.
|
||||
**
|
||||
** '?' Matches exactly one character.
|
||||
**
|
||||
** [...] Matches one character from the enclosed list of
|
||||
** characters.
|
||||
**
|
||||
** [^...] Matches one character not in the enclosed list.
|
||||
**
|
||||
*/
|
||||
static int quotaStrglob(const char *zGlob, const char *z){
|
||||
int c, c2;
|
||||
int invert;
|
||||
int seen;
|
||||
|
||||
while( (c = (*(zGlob++)))!=0 ){
|
||||
if( c=='*' ){
|
||||
while( (c=(*(zGlob++))) == '*' || c=='?' ){
|
||||
if( c=='?' && (*(z++))==0 ) return 0;
|
||||
}
|
||||
if( c==0 ){
|
||||
return 1;
|
||||
}else if( c=='[' ){
|
||||
while( *z && quotaStrglob(zGlob-1,z)==0 ){
|
||||
z++;
|
||||
}
|
||||
return (*z)!=0;
|
||||
}
|
||||
while( (c2 = (*(z++)))!=0 ){
|
||||
while( c2!=c ){
|
||||
c2 = *(z++);
|
||||
if( c2==0 ) return 0;
|
||||
}
|
||||
if( quotaStrglob(zGlob,z) ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}else if( c=='?' ){
|
||||
if( (*(z++))==0 ) return 0;
|
||||
}else if( c=='[' ){
|
||||
int prior_c = 0;
|
||||
seen = 0;
|
||||
invert = 0;
|
||||
c = *(z++);
|
||||
if( c==0 ) return 0;
|
||||
c2 = *(zGlob++);
|
||||
if( c2=='^' ){
|
||||
invert = 1;
|
||||
c2 = *(zGlob++);
|
||||
}
|
||||
if( c2==']' ){
|
||||
if( c==']' ) seen = 1;
|
||||
c2 = *(zGlob++);
|
||||
}
|
||||
while( c2 && c2!=']' ){
|
||||
if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
|
||||
c2 = *(zGlob++);
|
||||
if( c>=prior_c && c<=c2 ) seen = 1;
|
||||
prior_c = 0;
|
||||
}else{
|
||||
if( c==c2 ){
|
||||
seen = 1;
|
||||
}
|
||||
prior_c = c2;
|
||||
}
|
||||
c2 = *(zGlob++);
|
||||
}
|
||||
if( c2==0 || (seen ^ invert)==0 ) return 0;
|
||||
}else{
|
||||
if( c!=(*(z++)) ) return 0;
|
||||
}
|
||||
}
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
|
||||
/* Find a quotaGroup given the filename.
|
||||
**
|
||||
** Return a pointer to the quotaGroup object. Return NULL if not found.
|
||||
*/
|
||||
static quotaGroup *quotaGroupFind(const char *zFilename){
|
||||
quotaGroup *p;
|
||||
for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0;
|
||||
p=p->pNext){}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Translate an sqlite3_file* that is really a quotaConn* into
|
||||
** the sqlite3_file* for the underlying original VFS.
|
||||
*/
|
||||
static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
|
||||
quotaConn *p = (quotaConn*)pConn;
|
||||
return (sqlite3_file*)&p[1];
|
||||
}
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
/*
|
||||
** This is the xOpen method used for the "quota" VFS.
|
||||
**
|
||||
** Most of the work is done by the underlying original VFS. This method
|
||||
** simply links the new file into the appropriate quota group if it is a
|
||||
** file that needs to be tracked.
|
||||
*/
|
||||
static int quotaOpen(
|
||||
sqlite3_vfs *pVfs, /* The quota VFS */
|
||||
const char *zName, /* Name of file to be opened */
|
||||
sqlite3_file *pConn, /* Fill in this file descriptor */
|
||||
int flags, /* Flags to control the opening */
|
||||
int *pOutFlags /* Flags showing results of opening */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
quotaConn *pQuotaOpen; /* The new quota file descriptor */
|
||||
quotaFile *pFile; /* Corresponding quotaFile obj */
|
||||
quotaGroup *pGroup; /* The group file belongs to */
|
||||
sqlite3_file *pSubOpen; /* Real file descriptor */
|
||||
sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
|
||||
|
||||
/* If the file is not a main database file or a WAL, then use the
|
||||
** normal xOpen method.
|
||||
*/
|
||||
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
|
||||
return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
|
||||
}
|
||||
|
||||
/* If the name of the file does not match any quota group, then
|
||||
** use the normal xOpen method.
|
||||
*/
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zName);
|
||||
if( pGroup==0 ){
|
||||
rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
|
||||
}else{
|
||||
/* If we get to this point, it means the file needs to be quota tracked.
|
||||
*/
|
||||
pQuotaOpen = (quotaConn*)pConn;
|
||||
pSubOpen = quotaSubOpen(pConn);
|
||||
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName);
|
||||
pFile=pFile->pNext){}
|
||||
if( pFile==0 ){
|
||||
int nName = strlen(zName);
|
||||
pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
if( pFile==0 ){
|
||||
quotaLeave();
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
pFile->zFilename = (char*)&pFile[1];
|
||||
memcpy(pFile->zFilename, zName, nName+1);
|
||||
pFile->pNext = pGroup->pFiles;
|
||||
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
|
||||
pFile->ppPrev = &pGroup->pFiles;
|
||||
pGroup->pFiles = pFile;
|
||||
pFile->pGroup = pGroup;
|
||||
}
|
||||
pFile->nRef++;
|
||||
pQuotaOpen->pFile = pFile;
|
||||
if( pSubOpen->pMethods->iVersion==1 ){
|
||||
pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
|
||||
}else{
|
||||
pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
|
||||
}
|
||||
}
|
||||
}
|
||||
quotaLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/************************ I/O Method Wrappers *******************************/
|
||||
|
||||
/* xClose requests get passed through to the original VFS. But we
|
||||
** also have to unlink the quotaConn from the quotaFile and quotaGroup.
|
||||
** The quotaFile and/or quotaGroup are freed if they are no longer in use.
|
||||
*/
|
||||
static int quotaClose(sqlite3_file *pConn){
|
||||
quotaConn *p = (quotaConn*)pConn;
|
||||
quotaFile *pFile = p->pFile;
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
int rc;
|
||||
rc = pSubOpen->pMethods->xClose(pSubOpen);
|
||||
quotaEnter();
|
||||
pFile->nRef--;
|
||||
if( pFile->nRef==0 ){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
pGroup->iSize -= pFile->iSize;
|
||||
if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
|
||||
*pFile->ppPrev = pFile->pNext;
|
||||
quotaGroupDeref(pGroup);
|
||||
sqlite3_free(pFile);
|
||||
}
|
||||
quotaLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xRead requests directory thru to the original VFS without
|
||||
** further processing.
|
||||
*/
|
||||
static int quotaRead(
|
||||
sqlite3_file *pConn,
|
||||
void *pBuf,
|
||||
int iAmt,
|
||||
sqlite3_int64 iOfst
|
||||
){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/* Check xWrite requests to see if they expand the file. If they do,
|
||||
** the perform a quota check before passing them through to the
|
||||
** original VFS.
|
||||
*/
|
||||
static int quotaWrite(
|
||||
sqlite3_file *pConn,
|
||||
const void *pBuf,
|
||||
int iAmt,
|
||||
sqlite3_int64 iOfst
|
||||
){
|
||||
quotaConn *p = (quotaConn*)pConn;
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
sqlite3_int64 iEnd = iOfst+iAmt;
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile = p->pFile;
|
||||
sqlite3_int64 szNew;
|
||||
|
||||
if( pFile->iSize<iEnd ){
|
||||
pGroup = pFile->pGroup;
|
||||
quotaEnter();
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
if( pGroup->xCallback ){
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->pArg);
|
||||
}
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
quotaLeave();
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
}
|
||||
pGroup->iSize = szNew;
|
||||
pFile->iSize = iEnd;
|
||||
quotaLeave();
|
||||
}
|
||||
return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/* Pass xTruncate requests thru to the original VFS. If the
|
||||
** success, update the file size.
|
||||
*/
|
||||
static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){
|
||||
quotaConn *p = (quotaConn*)pConn;
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
|
||||
quotaFile *pFile = p->pFile;
|
||||
quotaGroup *pGroup;
|
||||
if( rc==SQLITE_OK ){
|
||||
quotaEnter();
|
||||
pGroup = pFile->pGroup;
|
||||
pGroup->iSize -= pFile->iSize;
|
||||
pFile->iSize = size;
|
||||
pGroup->iSize += size;
|
||||
quotaLeave();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xSync requests through to the original VFS without change
|
||||
*/
|
||||
static int quotaSync(sqlite3_file *pConn, int flags){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xSync(pSubOpen, flags);
|
||||
}
|
||||
|
||||
/* Pass xFileSize requests through to the original VFS but then
|
||||
** update the quotaGroup with the new size before returning.
|
||||
*/
|
||||
static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
||||
quotaConn *p = (quotaConn*)pConn;
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
quotaFile *pFile = p->pFile;
|
||||
quotaGroup *pGroup;
|
||||
sqlite3_int64 sz;
|
||||
int rc;
|
||||
|
||||
rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc==SQLITE_OK ){
|
||||
quotaEnter();
|
||||
pGroup = pFile->pGroup;
|
||||
pGroup->iSize -= pFile->iSize;
|
||||
pFile->iSize = sz;
|
||||
pGroup->iSize += sz;
|
||||
quotaLeave();
|
||||
*pSize = sz;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaLock(sqlite3_file *pConn, int lock){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xLock(pSubOpen, lock);
|
||||
}
|
||||
|
||||
/* Pass xUnlock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaUnlock(sqlite3_file *pConn, int lock){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
|
||||
}
|
||||
|
||||
/* Pass xCheckReservedLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
|
||||
}
|
||||
|
||||
/* Pass xFileControl requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
}
|
||||
|
||||
/* Pass xSectorSize requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaSectorSize(sqlite3_file *pConn){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xSectorSize(pSubOpen);
|
||||
}
|
||||
|
||||
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaDeviceCharacteristics(sqlite3_file *pConn){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
|
||||
}
|
||||
|
||||
/* Pass xShmMap requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaShmMap(
|
||||
sqlite3_file *pConn, /* Handle open on database file */
|
||||
int iRegion, /* Region to retrieve */
|
||||
int szRegion, /* Size of regions */
|
||||
int bExtend, /* True to extend file if necessary */
|
||||
void volatile **pp /* OUT: Mapped memory */
|
||||
){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
|
||||
}
|
||||
|
||||
/* Pass xShmLock requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaShmLock(
|
||||
sqlite3_file *pConn, /* Database file holding the shared memory */
|
||||
int ofst, /* First lock to acquire or release */
|
||||
int n, /* Number of locks to acquire or release */
|
||||
int flags /* What to do with the lock */
|
||||
){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
|
||||
}
|
||||
|
||||
/* Pass xShmBarrier requests through to the original VFS unchanged.
|
||||
*/
|
||||
static void quotaShmBarrier(sqlite3_file *pConn){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
pSubOpen->pMethods->xShmBarrier(pSubOpen);
|
||||
}
|
||||
|
||||
/* Pass xShmUnmap requests through to the original VFS unchanged.
|
||||
*/
|
||||
static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
|
||||
}
|
||||
|
||||
/************************** Public Interfaces *****************************/
|
||||
/*
|
||||
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The quota VFS shim is named "quota". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
sqlite3_vfs *pOrigVfs;
|
||||
if( gQuota.isInitialized ) return SQLITE_MISUSE;
|
||||
pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
|
||||
if( pOrigVfs==0 ) return SQLITE_ERROR;
|
||||
assert( pOrigVfs!=&gQuota.sThisVfs );
|
||||
gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
if( !gQuota.pMutex ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
gQuota.isInitialized = 1;
|
||||
gQuota.pOrigVfs = pOrigVfs;
|
||||
gQuota.sThisVfs = *pOrigVfs;
|
||||
gQuota.sThisVfs.xOpen = quotaOpen;
|
||||
gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
|
||||
gQuota.sThisVfs.zName = "quota";
|
||||
gQuota.sIoMethodsV1.iVersion = 1;
|
||||
gQuota.sIoMethodsV1.xClose = quotaClose;
|
||||
gQuota.sIoMethodsV1.xRead = quotaRead;
|
||||
gQuota.sIoMethodsV1.xWrite = quotaWrite;
|
||||
gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
|
||||
gQuota.sIoMethodsV1.xSync = quotaSync;
|
||||
gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
|
||||
gQuota.sIoMethodsV1.xLock = quotaLock;
|
||||
gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
|
||||
gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
|
||||
gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
|
||||
gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize;
|
||||
gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
|
||||
gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
|
||||
gQuota.sIoMethodsV2.iVersion = 2;
|
||||
gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
|
||||
gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
|
||||
gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
|
||||
gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
|
||||
sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Shutdown the quota system.
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly one while
|
||||
** shutting down in order to free all remaining quota groups.
|
||||
*/
|
||||
int sqlite3_quota_shutdown(void){
|
||||
quotaGroup *pGroup;
|
||||
if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
|
||||
for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
|
||||
if( pGroup->pFiles ) return SQLITE_MISUSE;
|
||||
}
|
||||
while( gQuota.pGroup ){
|
||||
pGroup = gQuota.pGroup;
|
||||
gQuota.pGroup = pGroup->pNext;
|
||||
pGroup->iLimit = 0;
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
gQuota.isInitialized = 0;
|
||||
sqlite3_mutex_free(gQuota.pMutex);
|
||||
sqlite3_vfs_unregister(&gQuota.sThisVfs);
|
||||
memset(&gQuota, 0, sizeof(gQuota));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create or destroy a quota group.
|
||||
**
|
||||
** The quota group is defined by the zPattern. When calling this routine
|
||||
** with a zPattern for a quota group that already exists, this routine
|
||||
** merely updates the iLimit, xCallback, and pArg values for that quota
|
||||
** group. If zPattern is new, then a new quota group is created.
|
||||
**
|
||||
** If the iLimit for a quota group is set to zero, then the quota group
|
||||
** is disabled and will be deleted when the last database connection using
|
||||
** the quota group is closed.
|
||||
**
|
||||
** Calling this routine on a zPattern that does not exist and with a
|
||||
** zero iLimit is a no-op.
|
||||
**
|
||||
** A quota group must exist with a non-zero iLimit prior to opening
|
||||
** database connections if those connections are to participate in the
|
||||
** quota group. Creating a quota group does not affect database connections
|
||||
** that are already open.
|
||||
*/
|
||||
int sqlite3_quota_set(
|
||||
const char *zPattern, /* The filename pattern */
|
||||
sqlite3_int64 iLimit, /* New quota to set for this quota group */
|
||||
void (*xCallback)( /* Callback invoked when going over quota */
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
),
|
||||
void *pArg, /* client data passed thru to callback */
|
||||
void (*xDestroy)(void*) /* Optional destructor for pArg */
|
||||
){
|
||||
quotaGroup *pGroup;
|
||||
quotaEnter();
|
||||
pGroup = gQuota.pGroup;
|
||||
while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
if( pGroup==0 ){
|
||||
int nPattern = strlen(zPattern);
|
||||
if( iLimit<=0 ){
|
||||
quotaLeave();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
|
||||
if( pGroup==0 ){
|
||||
quotaLeave();
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pGroup, 0, sizeof(*pGroup));
|
||||
pGroup->zPattern = (char*)&pGroup[1];
|
||||
memcpy((char *)pGroup->zPattern, zPattern, nPattern+1);
|
||||
if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
|
||||
pGroup->pNext = gQuota.pGroup;
|
||||
pGroup->ppPrev = &gQuota.pGroup;
|
||||
gQuota.pGroup = pGroup;
|
||||
}
|
||||
pGroup->iLimit = iLimit;
|
||||
pGroup->xCallback = xCallback;
|
||||
if( pGroup->xDestroy && pGroup->pArg!=pArg ){
|
||||
pGroup->xDestroy(pGroup->pArg);
|
||||
}
|
||||
pGroup->pArg = pArg;
|
||||
pGroup->xDestroy = xDestroy;
|
||||
quotaGroupDeref(pGroup);
|
||||
quotaLeave();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
|
||||
/*
|
||||
** Argument passed to a TCL quota-over-limit callback.
|
||||
*/
|
||||
typedef struct TclQuotaCallback TclQuotaCallback;
|
||||
struct TclQuotaCallback {
|
||||
Tcl_Interp *interp; /* Interpreter in which to run the script */
|
||||
Tcl_Obj *pScript; /* Script to be run */
|
||||
};
|
||||
|
||||
extern const char *sqlite3TestErrorName(int);
|
||||
|
||||
|
||||
/*
|
||||
** This is the callback from a quota-over-limit.
|
||||
*/
|
||||
static void tclQuotaCallback(
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
){
|
||||
TclQuotaCallback *p; /* Callback script object */
|
||||
Tcl_Obj *pEval; /* Script to evaluate */
|
||||
Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */
|
||||
unsigned int rnd; /* Random part of pVarname */
|
||||
int rc; /* Tcl error code */
|
||||
|
||||
p = (TclQuotaCallback *)pArg;
|
||||
if( p==0 ) return;
|
||||
|
||||
pVarname = Tcl_NewStringObj("::piLimit_", -1);
|
||||
Tcl_IncrRefCount(pVarname);
|
||||
sqlite3_randomness(sizeof(rnd), (void *)&rnd);
|
||||
Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF)));
|
||||
Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0);
|
||||
|
||||
pEval = Tcl_DuplicateObj(p->pScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1));
|
||||
Tcl_ListObjAppendElement(0, pEval, pVarname);
|
||||
Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize));
|
||||
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
|
||||
if( rc==TCL_OK ){
|
||||
Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0);
|
||||
rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit);
|
||||
Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0);
|
||||
}
|
||||
|
||||
Tcl_DecrRefCount(pEval);
|
||||
Tcl_DecrRefCount(pVarname);
|
||||
if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a TCL quota-over-limit callback.
|
||||
*/
|
||||
static void tclCallbackDestructor(void *pObj){
|
||||
TclQuotaCallback *p = (TclQuotaCallback*)pObj;
|
||||
if( p ){
|
||||
Tcl_DecrRefCount(p->pScript);
|
||||
sqlite3_free((char *)p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
|
||||
*/
|
||||
static int test_quota_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zName; /* Name of new quota VFS */
|
||||
int makeDefault; /* True to make the new VFS the default */
|
||||
int rc; /* Value returned by quota_initialize() */
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zName = Tcl_GetString(objv[1]);
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
|
||||
if( zName[0]=='\0' ) zName = 0;
|
||||
|
||||
/* Call sqlite3_quota_initialize() */
|
||||
rc = sqlite3_quota_initialize(zName, makeDefault);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_shutdown
|
||||
*/
|
||||
static int test_quota_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc; /* Value returned by quota_shutdown() */
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Call sqlite3_quota_shutdown() */
|
||||
rc = sqlite3_quota_shutdown();
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
|
||||
*/
|
||||
static int test_quota_set(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zPattern; /* File pattern to configure */
|
||||
sqlite3_int64 iLimit; /* Initial quota in bytes */
|
||||
Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */
|
||||
int rc; /* Value returned by quota_set() */
|
||||
TclQuotaCallback *p; /* Callback object */
|
||||
int nScript; /* Length of callback script */
|
||||
void (*xDestroy)(void*); /* Optional destructor for pArg */
|
||||
void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *);
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zPattern = Tcl_GetString(objv[1]);
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR;
|
||||
pScript = objv[3];
|
||||
Tcl_GetStringFromObj(pScript, &nScript);
|
||||
|
||||
if( nScript>0 ){
|
||||
/* Allocate a TclQuotaCallback object */
|
||||
p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback));
|
||||
if( !p ){
|
||||
Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
memset(p, 0, sizeof(TclQuotaCallback));
|
||||
p->interp = interp;
|
||||
Tcl_IncrRefCount(pScript);
|
||||
p->pScript = pScript;
|
||||
xDestroy = tclCallbackDestructor;
|
||||
xCallback = tclQuotaCallback;
|
||||
}else{
|
||||
p = 0;
|
||||
xDestroy = 0;
|
||||
xCallback = 0;
|
||||
}
|
||||
|
||||
/* Invoke sqlite3_quota_set() */
|
||||
rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_dump
|
||||
*/
|
||||
static int test_quota_dump(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
Tcl_Obj *pResult;
|
||||
Tcl_Obj *pGroupTerm;
|
||||
Tcl_Obj *pFileTerm;
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
|
||||
pResult = Tcl_NewObj();
|
||||
quotaEnter();
|
||||
for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
|
||||
pGroupTerm = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewStringObj(pGroup->zPattern, -1));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewWideIntObj(pGroup->iLimit));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewWideIntObj(pGroup->iSize));
|
||||
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
|
||||
pFileTerm = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewStringObj(pFile->zFilename, -1));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewWideIntObj(pFile->iSize));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewWideIntObj(pFile->nRef));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
|
||||
}
|
||||
quotaLeave();
|
||||
Tcl_SetObjResult(interp, pResult);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine registers the custom TCL commands defined in this
|
||||
** module. This should be the only procedure visible from outside
|
||||
** of this module.
|
||||
*/
|
||||
int Sqlitequota_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
** 2010 August 28
|
||||
**
|
||||
** 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 all sorts of SQLite interfaces. This code
|
||||
** is not included in the SQLite library.
|
||||
*/
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Type used to cache parameter information for the "circle" r-tree geometry
|
||||
** callback.
|
||||
*/
|
||||
typedef struct Circle Circle;
|
||||
struct Circle {
|
||||
struct Box {
|
||||
double xmin;
|
||||
double xmax;
|
||||
double ymin;
|
||||
double ymax;
|
||||
} aBox[2];
|
||||
double centerx;
|
||||
double centery;
|
||||
double radius;
|
||||
};
|
||||
|
||||
/*
|
||||
** Destructor function for Circle objects allocated by circle_geom().
|
||||
*/
|
||||
static void circle_del(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of "circle" r-tree geometry callback.
|
||||
*/
|
||||
static int circle_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
double *aCoord,
|
||||
int *pRes
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
Circle *pCircle; /* Structure defining circular region */
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
|
||||
if( p->pUser==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
/* This geometry callback is for use with a 2-dimensional r-tree table.
|
||||
** Return an error if the table does not have exactly 2 dimensions. */
|
||||
if( nCoord!=4 ) return SQLITE_ERROR;
|
||||
|
||||
/* Test that the correct number of parameters (3) have been supplied,
|
||||
** and that the parameters are in range (that the radius of the circle
|
||||
** radius is greater than zero). */
|
||||
if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
|
||||
|
||||
/* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
|
||||
** if the allocation fails. */
|
||||
pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
|
||||
if( !pCircle ) return SQLITE_NOMEM;
|
||||
p->xDelUser = circle_del;
|
||||
|
||||
/* Record the center and radius of the circular region. One way that
|
||||
** tested bounding boxes that intersect the circular region are detected
|
||||
** is by testing if each corner of the bounding box lies within radius
|
||||
** units of the center of the circle. */
|
||||
pCircle->centerx = p->aParam[0];
|
||||
pCircle->centery = p->aParam[1];
|
||||
pCircle->radius = p->aParam[2];
|
||||
|
||||
/* Define two bounding box regions. The first, aBox[0], extends to
|
||||
** infinity in the X dimension. It covers the same range of the Y dimension
|
||||
** as the circular region. The second, aBox[1], extends to infinity in
|
||||
** the Y dimension and is constrained to the range of the circle in the
|
||||
** X dimension.
|
||||
**
|
||||
** Then imagine each box is split in half along its short axis by a line
|
||||
** that intersects the center of the circular region. A bounding box
|
||||
** being tested can be said to intersect the circular region if it contains
|
||||
** points from each half of either of the two infinite bounding boxes.
|
||||
*/
|
||||
pCircle->aBox[0].xmin = pCircle->centerx;
|
||||
pCircle->aBox[0].xmax = pCircle->centerx;
|
||||
pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
|
||||
pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
|
||||
pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
}
|
||||
|
||||
pCircle = (Circle *)p->pUser;
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
** return SQLITE_OK in this case. */
|
||||
for(i=0; i<4; i++){
|
||||
double x = (i&0x01) ? xmax : xmin;
|
||||
double y = (i&0x02) ? ymax : ymin;
|
||||
double d2;
|
||||
|
||||
d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
|
||||
d2 += (y-pCircle->centery)*(y-pCircle->centery);
|
||||
if( d2<(pCircle->radius*pCircle->radius) ){
|
||||
*pRes = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the bounding box covers any other part of the circular region.
|
||||
** See comments above for a description of how this test works. If it does
|
||||
** cover part of the circular region, set the output variable to true
|
||||
** and return SQLITE_OK. */
|
||||
for(i=0; i<2; i++){
|
||||
if( xmin<=pCircle->aBox[i].xmin
|
||||
&& xmax>=pCircle->aBox[i].xmax
|
||||
&& ymin<=pCircle->aBox[i].ymin
|
||||
&& ymax>=pCircle->aBox[i].ymax
|
||||
){
|
||||
*pRes = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* The specified bounding box does not intersect the circular region. Set
|
||||
** the output variable to zero and return SQLITE_OK. */
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* END of implementation of "circle" geometry callback.
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include "tcl.h"
|
||||
|
||||
typedef struct Cube Cube;
|
||||
struct Cube {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double width;
|
||||
double height;
|
||||
double depth;
|
||||
};
|
||||
|
||||
static void cube_context_free(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The context pointer registered along with the 'cube' callback is
|
||||
** always ((void *)&gHere). This is just to facilitate testing, it is not
|
||||
** actually used for anything.
|
||||
*/
|
||||
static int gHere = 42;
|
||||
|
||||
/*
|
||||
** Implementation of a simple r-tree geom callback to test for intersection
|
||||
** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
|
||||
** coordinates as follows:
|
||||
**
|
||||
** cube(x, y, z, width, height, depth)
|
||||
**
|
||||
** The width, height and depth parameters must all be greater than zero.
|
||||
*/
|
||||
static int cube_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
double *aCoord,
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
|
||||
assert( p->pContext==(void *)&gHere );
|
||||
|
||||
if( pCube==0 ){
|
||||
if( p->nParam!=6 || nCoord!=6
|
||||
|| p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
|
||||
){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
|
||||
if( !pCube ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCube->x = p->aParam[0];
|
||||
pCube->y = p->aParam[1];
|
||||
pCube->z = p->aParam[2];
|
||||
pCube->width = p->aParam[3];
|
||||
pCube->height = p->aParam[4];
|
||||
pCube->depth = p->aParam[5];
|
||||
|
||||
p->pUser = (void *)pCube;
|
||||
p->xDelUser = cube_context_free;
|
||||
}
|
||||
|
||||
assert( nCoord==6 );
|
||||
*piRes = 0;
|
||||
if( aCoord[0]<=(pCube->x+pCube->width)
|
||||
&& aCoord[1]>=pCube->x
|
||||
&& aCoord[2]<=(pCube->y+pCube->height)
|
||||
&& aCoord[3]>=pCube->y
|
||||
&& aCoord[4]<=(pCube->z+pCube->depth)
|
||||
&& aCoord[5]>=pCube->z
|
||||
){
|
||||
*piRes = 1;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int register_cube_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_ENABLE_RTREE
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(interp);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
#else
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3TestErrorName(int);
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int register_circle_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_ENABLE_RTREE
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(interp);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
#else
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3TestErrorName(int);
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestrtree_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0);
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
** 2010 November 19
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Example code for obtaining an exclusive lock on an SQLite database
|
||||
** file. This method is complicated, but works for both WAL and rollback
|
||||
** mode database files. The interface to the example code in this file
|
||||
** consists of the following two functions:
|
||||
**
|
||||
** sqlite3demo_superlock()
|
||||
** sqlite3demo_superunlock()
|
||||
*/
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string.h> /* memset(), strlen() */
|
||||
#include <assert.h> /* assert() */
|
||||
|
||||
/*
|
||||
** A structure to collect a busy-handler callback and argument and a count
|
||||
** of the number of times it has been invoked.
|
||||
*/
|
||||
struct SuperlockBusy {
|
||||
int (*xBusy)(void*,int); /* Pointer to busy-handler function */
|
||||
void *pBusyArg; /* First arg to pass to xBusy */
|
||||
int nBusy; /* Number of times xBusy has been invoked */
|
||||
};
|
||||
typedef struct SuperlockBusy SuperlockBusy;
|
||||
|
||||
/*
|
||||
** An instance of the following structure is allocated for each active
|
||||
** superlock. The opaque handle returned by sqlite3demo_superlock() is
|
||||
** actually a pointer to an instance of this structure.
|
||||
*/
|
||||
struct Superlock {
|
||||
sqlite3 *db; /* Database handle used to lock db */
|
||||
int bWal; /* True if db is a WAL database */
|
||||
};
|
||||
typedef struct Superlock Superlock;
|
||||
|
||||
/*
|
||||
** The pCtx pointer passed to this function is actually a pointer to a
|
||||
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
|
||||
** by the structure and return the result.
|
||||
*/
|
||||
static int superlockBusyHandler(void *pCtx, int UNUSED){
|
||||
SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
|
||||
if( pBusy->xBusy==0 ) return 0;
|
||||
return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to determine if the main database file for
|
||||
** connection db is open in WAL mode or not. If no error occurs and the
|
||||
** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
|
||||
** If it is not in WAL mode, set *pbWal to false.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code. The value of *pbWal
|
||||
** is undefined in this case.
|
||||
*/
|
||||
static int superlockIsWal(Superlock *pLock){
|
||||
int rc; /* Return Code */
|
||||
sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */
|
||||
|
||||
rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
pLock->bWal = 0;
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
|
||||
pLock->bWal = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
|
||||
** of the file fd. If the lock cannot be obtained immediately, invoke
|
||||
** the busy-handler until either it is obtained or the busy-handler
|
||||
** callback returns 0.
|
||||
*/
|
||||
static int superlockShmLock(
|
||||
sqlite3_file *fd, /* Database file handle */
|
||||
int idx, /* Offset of shm-lock to obtain */
|
||||
int nByte, /* Number of consective bytes to lock */
|
||||
SuperlockBusy *pBusy /* Busy-handler wrapper object */
|
||||
){
|
||||
int rc;
|
||||
int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
|
||||
do {
|
||||
rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
|
||||
}while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain the extra locks on the database file required for WAL databases.
|
||||
** Invoke the supplied busy-handler as required.
|
||||
*/
|
||||
static int superlockWalLock(
|
||||
sqlite3 *db, /* Database handle open on WAL database */
|
||||
SuperlockBusy *pBusy /* Busy handler wrapper object */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
sqlite3_file *fd = 0; /* Main database file handle */
|
||||
void volatile *p = 0; /* Pointer to first page of shared memory */
|
||||
|
||||
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
|
||||
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
|
||||
** clients running database recovery.
|
||||
*/
|
||||
rc = superlockShmLock(fd, 2, 1, pBusy);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Zero the start of the first shared-memory page. This means that any
|
||||
** clients that open read or write transactions from this point on will
|
||||
** have to run recovery before proceeding. Since they need the "recovery"
|
||||
** lock that this process is holding to do that, no new read or write
|
||||
** transactions may now be opened. Nor can a checkpoint be run, for the
|
||||
** same reason.
|
||||
*/
|
||||
rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
memset((void *)p, 0, 32);
|
||||
|
||||
/* Obtain exclusive locks on all the "read-lock" slots. Once these locks
|
||||
** are held, it is guaranteed that there are no active reader, writer or
|
||||
** checkpointer clients.
|
||||
*/
|
||||
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release a superlock held on a database file. The argument passed to
|
||||
** this function must have been obtained from a successful call to
|
||||
** sqlite3demo_superlock().
|
||||
*/
|
||||
void sqlite3demo_superunlock(void *pLock){
|
||||
Superlock *p = (Superlock *)pLock;
|
||||
if( p->bWal ){
|
||||
int rc; /* Return code */
|
||||
int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
|
||||
sqlite3_file *fd = 0;
|
||||
rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xShmLock(fd, 2, 1, flags);
|
||||
fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
|
||||
}
|
||||
}
|
||||
sqlite3_close(p->db);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain a superlock on the database file identified by zPath, using the
|
||||
** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
|
||||
** returned and output variable *ppLock is populated with an opaque handle
|
||||
** that may be used with sqlite3demo_superunlock() to release the lock.
|
||||
**
|
||||
** If an error occurs, *ppLock is set to 0 and an SQLite error code
|
||||
** (e.g. SQLITE_BUSY) is returned.
|
||||
**
|
||||
** If a required lock cannot be obtained immediately and the xBusy parameter
|
||||
** to this function is not NULL, then xBusy is invoked in the same way
|
||||
** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
|
||||
** until either the lock can be obtained or the busy-handler function returns
|
||||
** 0 (indicating "give up").
|
||||
*/
|
||||
int sqlite3demo_superlock(
|
||||
const char *zPath, /* Path to database file to lock */
|
||||
const char *zVfs, /* VFS to use to access database file */
|
||||
int (*xBusy)(void*,int), /* Busy handler callback */
|
||||
void *pBusyArg, /* Context arg for busy handler */
|
||||
void **ppLock /* OUT: Context to pass to superunlock() */
|
||||
){
|
||||
SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
|
||||
int rc; /* Return code */
|
||||
Superlock *pLock;
|
||||
|
||||
pLock = sqlite3_malloc(sizeof(Superlock));
|
||||
if( !pLock ) return SQLITE_NOMEM;
|
||||
memset(pLock, 0, sizeof(Superlock));
|
||||
|
||||
/* Open a database handle on the file to superlock. */
|
||||
rc = sqlite3_open_v2(
|
||||
zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
|
||||
);
|
||||
|
||||
/* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
|
||||
** a WAL database, this is all we need to do.
|
||||
**
|
||||
** A wrapper function is used to invoke the busy-handler instead of
|
||||
** registering the busy-handler function supplied by the user directly
|
||||
** with SQLite. This is because the same busy-handler function may be
|
||||
** invoked directly later on when attempting to obtain the extra locks
|
||||
** required in WAL mode. By using the wrapper, we are able to guarantee
|
||||
** that the "nBusy" integer parameter passed to the users busy-handler
|
||||
** represents the total number of busy-handler invocations made within
|
||||
** this call to sqlite3demo_superlock(), including any made during the
|
||||
** "BEGIN EXCLUSIVE".
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
busy.xBusy = xBusy;
|
||||
busy.pBusyArg = pBusyArg;
|
||||
sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy);
|
||||
rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0);
|
||||
}
|
||||
|
||||
/* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
|
||||
** database, call superlockWalLock() to obtain the extra locks required
|
||||
** to prevent readers, writers and/or checkpointers from accessing the
|
||||
** db while this process is holding the superlock.
|
||||
**
|
||||
** Before attempting any WAL locks, commit the transaction started above
|
||||
** to drop the WAL read and write locks currently held. Otherwise, the
|
||||
** new WAL locks may conflict with the old.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
|
||||
rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = superlockWalLock(pLock->db, &busy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3demo_superunlock(pLock);
|
||||
*ppLock = 0;
|
||||
}else{
|
||||
*ppLock = pLock;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** End of example code. Everything below here is the test harness.
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
|
||||
struct InterpAndScript {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
};
|
||||
typedef struct InterpAndScript InterpAndScript;
|
||||
|
||||
static void superunlock_del(ClientData cd){
|
||||
sqlite3demo_superunlock((void *)cd);
|
||||
}
|
||||
|
||||
static int superunlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int superlock_busy(void *pCtx, int nBusy){
|
||||
InterpAndScript *p = (InterpAndScript *)pCtx;
|
||||
Tcl_Obj *pEval; /* Script to evaluate */
|
||||
int iVal = 0; /* Value to return */
|
||||
|
||||
pEval = Tcl_DuplicateObj(p->pScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
|
||||
Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
|
||||
Tcl_DecrRefCount(pEval);
|
||||
|
||||
return iVal;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
|
||||
*/
|
||||
static int superlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void *pLock; /* Lock context */
|
||||
char *zPath;
|
||||
char *zVfs = 0;
|
||||
InterpAndScript busy = {0, 0};
|
||||
int (*xBusy)(void*,int) = 0; /* Busy handler callback */
|
||||
int rc; /* Return code from sqlite3demo_superlock() */
|
||||
|
||||
if( objc<3 || objc>5 ){
|
||||
Tcl_WrongNumArgs(
|
||||
interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zPath = Tcl_GetString(objv[2]);
|
||||
|
||||
if( objc>3 ){
|
||||
zVfs = Tcl_GetString(objv[3]);
|
||||
if( strlen(zVfs)==0 ) zVfs = 0;
|
||||
}
|
||||
if( objc>4 ){
|
||||
busy.interp = interp;
|
||||
busy.pScript = objv[4];
|
||||
xBusy = superlock_busy;
|
||||
}
|
||||
|
||||
rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
|
||||
assert( rc==SQLITE_OK || pLock==0 );
|
||||
assert( rc!=SQLITE_OK || pLock!=0 );
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
extern const char *sqlite3ErrStr(int);
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
|
||||
);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int SqliteSuperlock_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
@@ -26,6 +26,7 @@
|
||||
** -default BOOLEAN (True to make the vfs default. Default false)
|
||||
** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile)
|
||||
** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname)
|
||||
** -iversion INTEGER (Value for sqlite3_vfs.iVersion)
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
@@ -539,6 +540,7 @@ static int tvfsOpen(
|
||||
pFd->zFilename = zName;
|
||||
pFd->pVfs = pVfs;
|
||||
pFd->pReal = (sqlite3_file *)&pFd[1];
|
||||
memset(pTestfile, 0, sizeof(TestvfsFile));
|
||||
pTestfile->pFd = pFd;
|
||||
|
||||
/* Evaluate the Tcl script:
|
||||
|
||||
@@ -638,6 +638,7 @@ static void updateVirtualTable(
|
||||
assert( v );
|
||||
ephemTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
|
||||
/* fill the ephemeral table
|
||||
*/
|
||||
|
||||
+126
-148
@@ -215,6 +215,12 @@ int sqlite3Dequote(char *z){
|
||||
/*
|
||||
** Some systems have stricmp(). Others have strcasecmp(). Because
|
||||
** there is no consistency, we will define our own.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows
|
||||
** applications and extensions to compare the contents of two buffers
|
||||
** containing UTF-8 strings in a case-independent fashion, using the same
|
||||
** definition of case independence that SQLite uses internally when
|
||||
** comparing identifiers.
|
||||
*/
|
||||
int sqlite3StrICmp(const char *zLeft, const char *zRight){
|
||||
register unsigned char *a, *b;
|
||||
@@ -232,121 +238,111 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if z is a pure numeric string. Return FALSE and leave
|
||||
** *realnum unchanged if the string contains any character which is not
|
||||
** part of a number.
|
||||
** The string z[] is an text representation of a real number.
|
||||
** Convert this string to a double and write it into *pResult.
|
||||
**
|
||||
** If the string is pure numeric, set *realnum to TRUE if the string
|
||||
** contains the '.' character or an "E+000" style exponentiation suffix.
|
||||
** Otherwise set *realnum to FALSE. Note that just becaue *realnum is
|
||||
** false does not mean that the number can be successfully converted into
|
||||
** an integer - it might be too big.
|
||||
** The string z[] is length bytes in length (bytes, not characters) and
|
||||
** uses the encoding enc. The string is not necessarily zero-terminated.
|
||||
**
|
||||
** An empty string is considered non-numeric.
|
||||
** Return TRUE if the result is a valid real number (or integer) and FALSE
|
||||
** if the string is empty or contains extraneous text. Valid numbers
|
||||
** are in one of these formats:
|
||||
**
|
||||
** [+-]digits[E[+-]digits]
|
||||
** [+-]digits.[digits][E[+-]digits]
|
||||
** [+-].digits[E[+-]digits]
|
||||
**
|
||||
** Leading and trailing whitespace is ignored for the purpose of determining
|
||||
** validity.
|
||||
**
|
||||
** If some prefix of the input string is a valid number, this routine
|
||||
** returns FALSE but it still converts the prefix and writes the result
|
||||
** into *pResult.
|
||||
*/
|
||||
int sqlite3IsNumber(const char *z, int *realnum, u8 enc){
|
||||
int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int incr = (enc==SQLITE_UTF8?1:2);
|
||||
if( enc==SQLITE_UTF16BE ) z++;
|
||||
if( *z=='-' || *z=='+' ) z += incr;
|
||||
if( !sqlite3Isdigit(*z) ){
|
||||
return 0;
|
||||
}
|
||||
z += incr;
|
||||
*realnum = 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( *z=='.' ){
|
||||
z += incr;
|
||||
if( !sqlite3Isdigit(*z) ) return 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
*realnum = 1;
|
||||
}
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z += incr;
|
||||
if( *z=='+' || *z=='-' ) z += incr;
|
||||
if( !sqlite3Isdigit(*z) ) return 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
*realnum = 1;
|
||||
}
|
||||
#endif
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The string z[] is an ASCII representation of a real number.
|
||||
** Convert this string to a double.
|
||||
**
|
||||
** This routine assumes that z[] really is a valid number. If it
|
||||
** is not, the result is undefined.
|
||||
**
|
||||
** This routine is used instead of the library atof() function because
|
||||
** the library atof() might want to use "," as the decimal point instead
|
||||
** of "." depending on how locale is set. But that would cause problems
|
||||
** for SQL. So this routine always uses "." regardless of locale.
|
||||
*/
|
||||
int sqlite3AtoF(const char *z, double *pResult){
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
const char *zBegin = z;
|
||||
const char *zEnd = z + length;
|
||||
/* sign * significand * (10 ^ (esign * exponent)) */
|
||||
int sign = 1; /* sign of significand */
|
||||
i64 s = 0; /* significand */
|
||||
int d = 0; /* adjust exponent for shifting decimal point */
|
||||
int esign = 1; /* sign of exponent */
|
||||
int e = 0; /* exponent */
|
||||
int sign = 1; /* sign of significand */
|
||||
i64 s = 0; /* significand */
|
||||
int d = 0; /* adjust exponent for shifting decimal point */
|
||||
int esign = 1; /* sign of exponent */
|
||||
int e = 0; /* exponent */
|
||||
int eValid = 1; /* True exponent is either not used or is well-formed */
|
||||
double result;
|
||||
int nDigits = 0;
|
||||
|
||||
*pResult = 0.0; /* Default return value, in case of an error */
|
||||
|
||||
if( enc==SQLITE_UTF16BE ) z++;
|
||||
|
||||
/* skip leading spaces */
|
||||
while( sqlite3Isspace(*z) ) z++;
|
||||
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
||||
if( z>=zEnd ) return 0;
|
||||
|
||||
/* get sign of significand */
|
||||
if( *z=='-' ){
|
||||
sign = -1;
|
||||
z++;
|
||||
z+=incr;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
z+=incr;
|
||||
}
|
||||
|
||||
/* skip leading zeroes */
|
||||
while( z[0]=='0' ) z++, nDigits++;
|
||||
while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
|
||||
|
||||
/* copy max significant digits to significand */
|
||||
while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++;
|
||||
z+=incr, nDigits++;
|
||||
}
|
||||
|
||||
/* skip non-significant significand digits
|
||||
** (increase exponent by d to shift decimal left) */
|
||||
while( sqlite3Isdigit(*z) ) z++, nDigits++, d++;
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
|
||||
if( z>=zEnd ) goto do_atof_calc;
|
||||
|
||||
/* if decimal point is present */
|
||||
if( *z=='.' ){
|
||||
z++;
|
||||
z+=incr;
|
||||
/* copy digits from after decimal to significand
|
||||
** (decrease exponent by d to shift decimal right) */
|
||||
while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++, d--;
|
||||
z+=incr, nDigits++, d--;
|
||||
}
|
||||
/* skip non-significant digits */
|
||||
while( sqlite3Isdigit(*z) ) z++, nDigits++;
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++;
|
||||
}
|
||||
if( z>=zEnd ) goto do_atof_calc;
|
||||
|
||||
/* if exponent is present */
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z++;
|
||||
z+=incr;
|
||||
eValid = 0;
|
||||
if( z>=zEnd ) goto do_atof_calc;
|
||||
/* get sign of exponent */
|
||||
if( *z=='-' ){
|
||||
esign = -1;
|
||||
z++;
|
||||
z+=incr;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
z+=incr;
|
||||
}
|
||||
/* copy digits to exponent */
|
||||
while( sqlite3Isdigit(*z) ){
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){
|
||||
e = e*10 + (*z - '0');
|
||||
z++;
|
||||
z+=incr;
|
||||
eValid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* skip trailing spaces */
|
||||
if( nDigits && eValid ){
|
||||
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
||||
}
|
||||
|
||||
do_atof_calc:
|
||||
/* adjust exponent by d, and update sign */
|
||||
e = (e*esign) + d;
|
||||
if( e<0 ) {
|
||||
@@ -405,10 +401,10 @@ int sqlite3AtoF(const char *z, double *pResult){
|
||||
/* store the result */
|
||||
*pResult = result;
|
||||
|
||||
/* return number of characters used */
|
||||
return (int)(z - zBegin);
|
||||
/* return true if number and no extra non-whitespace chracters after */
|
||||
return z>=zEnd && nDigits>0 && eValid;
|
||||
#else
|
||||
return sqlite3Atoi64(z, pResult);
|
||||
return !sqlite3Atoi64(z, pResult, length, enc);
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
}
|
||||
|
||||
@@ -416,20 +412,26 @@ int sqlite3AtoF(const char *z, double *pResult){
|
||||
** Compare the 19-character string zNum against the text representation
|
||||
** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
||||
** if zNum is less than, equal to, or greater than the string.
|
||||
** Note that zNum must contain exactly 19 characters.
|
||||
**
|
||||
** Unlike memcmp() this routine is guaranteed to return the difference
|
||||
** in the values of the last digit if the only difference is in the
|
||||
** last digit. So, for example,
|
||||
**
|
||||
** compare2pow63("9223372036854775800")
|
||||
** compare2pow63("9223372036854775800", 1)
|
||||
**
|
||||
** will return -8.
|
||||
*/
|
||||
static int compare2pow63(const char *zNum){
|
||||
int c;
|
||||
c = memcmp(zNum,"922337203685477580",18)*10;
|
||||
static int compare2pow63(const char *zNum, int incr){
|
||||
int c = 0;
|
||||
int i;
|
||||
/* 012345678901234567 */
|
||||
const char *pow63 = "922337203685477580";
|
||||
for(i=0; c==0 && i<18; i++){
|
||||
c = (zNum[i*incr]-pow63[i])*10;
|
||||
}
|
||||
if( c==0 ){
|
||||
c = zNum[18] - '8';
|
||||
c = zNum[18*incr] - '8';
|
||||
testcase( c==(-1) );
|
||||
testcase( c==0 );
|
||||
testcase( c==(+1) );
|
||||
@@ -439,94 +441,60 @@ static int compare2pow63(const char *zNum){
|
||||
|
||||
|
||||
/*
|
||||
** Return TRUE if zNum is a 64-bit signed integer and write
|
||||
** the value of the integer into *pNum. If zNum is not an integer
|
||||
** or is an integer that is too large to be expressed with 64 bits,
|
||||
** then return false.
|
||||
** Convert zNum to a 64-bit signed integer and write
|
||||
** the value of the integer into *pNum.
|
||||
** If zNum is exactly 9223372036854665808, return 2.
|
||||
** This is a special case as the context will determine
|
||||
** if it is too big (used as a negative).
|
||||
** If zNum is not an integer or is an integer that
|
||||
** is too large to be expressed with 64 bits,
|
||||
** then return 1. Otherwise return 0.
|
||||
**
|
||||
** When this routine was originally written it dealt with only
|
||||
** 32-bit numbers. At that time, it was much faster than the
|
||||
** atoi() library routine in RedHat 7.2.
|
||||
** length is the number of bytes in the string (bytes, not characters).
|
||||
** The string is not necessarily zero-terminated. The encoding is
|
||||
** given by enc.
|
||||
*/
|
||||
int sqlite3Atoi64(const char *zNum, i64 *pNum){
|
||||
int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
int incr = (enc==SQLITE_UTF8?1:2);
|
||||
i64 v = 0;
|
||||
int neg;
|
||||
int i, c;
|
||||
int neg = 0; /* assume positive */
|
||||
int i;
|
||||
int c = 0;
|
||||
const char *zStart;
|
||||
while( sqlite3Isspace(*zNum) ) zNum++;
|
||||
const char *zEnd = zNum + length;
|
||||
if( enc==SQLITE_UTF16BE ) zNum++;
|
||||
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
|
||||
if( zNum>=zEnd ) goto do_atoi_calc;
|
||||
if( *zNum=='-' ){
|
||||
neg = 1;
|
||||
zNum++;
|
||||
zNum+=incr;
|
||||
}else if( *zNum=='+' ){
|
||||
neg = 0;
|
||||
zNum++;
|
||||
}else{
|
||||
neg = 0;
|
||||
zNum+=incr;
|
||||
}
|
||||
do_atoi_calc:
|
||||
zStart = zNum;
|
||||
while( zNum[0]=='0' ){ zNum++; } /* Skip over leading zeros. Ticket #2454 */
|
||||
for(i=0; (c=zNum[i])>='0' && c<='9'; i++){
|
||||
while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
|
||||
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
|
||||
v = v*10 + c - '0';
|
||||
}
|
||||
*pNum = neg ? -v : v;
|
||||
testcase( i==18 );
|
||||
testcase( i==19 );
|
||||
testcase( i==20 );
|
||||
if( c!=0 || (i==0 && zStart==zNum) || i>19 ){
|
||||
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
|
||||
/* zNum is empty or contains non-numeric text or is longer
|
||||
** than 19 digits (thus guaranting that it is too large) */
|
||||
return 0;
|
||||
}else if( i<19 ){
|
||||
/* Less than 19 digits, so we know that it fits in 64 bits */
|
||||
** than 19 digits (thus guaranteeing that it is too large) */
|
||||
return 1;
|
||||
}else if( i<19*incr ){
|
||||
/* Less than 19 digits, so we know that it fits in 64 bits */
|
||||
return 0;
|
||||
}else{
|
||||
/* 19-digit numbers must be no larger than 9223372036854775807 if positive
|
||||
** or 9223372036854775808 if negative. Note that 9223372036854665808
|
||||
** is 2^63. */
|
||||
return compare2pow63(zNum)<neg;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The string zNum represents an unsigned integer. The zNum string
|
||||
** consists of one or more digit characters and is terminated by
|
||||
** a zero character. Any stray characters in zNum result in undefined
|
||||
** behavior.
|
||||
**
|
||||
** If the unsigned integer that zNum represents will fit in a
|
||||
** 64-bit signed integer, return TRUE. Otherwise return FALSE.
|
||||
**
|
||||
** If the negFlag parameter is true, that means that zNum really represents
|
||||
** a negative number. (The leading "-" is omitted from zNum.) This
|
||||
** parameter is needed to determine a boundary case. A string
|
||||
** of "9223373036854775808" returns false if negFlag is false or true
|
||||
** if negFlag is true.
|
||||
**
|
||||
** Leading zeros are ignored.
|
||||
*/
|
||||
int sqlite3FitsIn64Bits(const char *zNum, int negFlag){
|
||||
int i;
|
||||
int neg = 0;
|
||||
|
||||
assert( zNum[0]>='0' && zNum[0]<='9' ); /* zNum is an unsigned number */
|
||||
|
||||
if( negFlag ) neg = 1-neg;
|
||||
while( *zNum=='0' ){
|
||||
zNum++; /* Skip leading zeros. Ticket #2454 */
|
||||
}
|
||||
for(i=0; zNum[i]; i++){ assert( zNum[i]>='0' && zNum[i]<='9' ); }
|
||||
testcase( i==18 );
|
||||
testcase( i==19 );
|
||||
testcase( i==20 );
|
||||
if( i<19 ){
|
||||
/* Guaranteed to fit if less than 19 digits */
|
||||
return 1;
|
||||
}else if( i>19 ){
|
||||
/* Guaranteed to be too big if greater than 19 digits */
|
||||
return 0;
|
||||
}else{
|
||||
/* Compare against 2^63. */
|
||||
return compare2pow63(zNum)<neg;
|
||||
** is 2^63. Return 1 if to large */
|
||||
c=compare2pow63(zNum, incr);
|
||||
if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */
|
||||
return c<neg ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,6 +541,16 @@ int sqlite3GetInt32(const char *zNum, int *pValue){
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a 32-bit integer value extracted from a string. If the
|
||||
** string is not an integer, just return 0.
|
||||
*/
|
||||
int sqlite3Atoi(const char *z){
|
||||
int x = 0;
|
||||
if( z ) sqlite3GetInt32(z, &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
** The variable-length integer encoding is as follows:
|
||||
**
|
||||
|
||||
@@ -110,6 +110,10 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( db->activeVdbeCnt>1 ){
|
||||
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Save the current value of the database flags so that it can be
|
||||
** restored before returning. Then set the writable-schema flag, and
|
||||
|
||||
+167
-75
@@ -46,6 +46,17 @@
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
/*
|
||||
** Invoke this macro on memory cells just prior to changing the
|
||||
** value of the cell. This macro verifies that shallow copies are
|
||||
** not misused.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M)
|
||||
#else
|
||||
# define memAboutToChange(P,M)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following global variable is incremented every time a cursor
|
||||
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test
|
||||
@@ -238,31 +249,17 @@ static VdbeCursor *allocateCursor(
|
||||
*/
|
||||
static void applyNumericAffinity(Mem *pRec){
|
||||
if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
|
||||
int realnum;
|
||||
double rValue;
|
||||
i64 iValue;
|
||||
u8 enc = pRec->enc;
|
||||
sqlite3VdbeMemNulTerminate(pRec);
|
||||
if( (pRec->flags&MEM_Str) && sqlite3IsNumber(pRec->z, &realnum, enc) ){
|
||||
i64 value;
|
||||
char *zUtf8 = pRec->z;
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
assert( pRec->db );
|
||||
zUtf8 = sqlite3Utf16to8(pRec->db, pRec->z, pRec->n, enc);
|
||||
if( !zUtf8 ) return;
|
||||
}
|
||||
#endif
|
||||
if( !realnum && sqlite3Atoi64(zUtf8, &value) ){
|
||||
pRec->u.i = value;
|
||||
MemSetTypeFlag(pRec, MEM_Int);
|
||||
}else{
|
||||
sqlite3AtoF(zUtf8, &pRec->r);
|
||||
MemSetTypeFlag(pRec, MEM_Real);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
sqlite3DbFree(pRec->db, zUtf8);
|
||||
}
|
||||
#endif
|
||||
if( (pRec->flags&MEM_Str)==0 ) return;
|
||||
if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
|
||||
if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
|
||||
pRec->u.i = iValue;
|
||||
pRec->flags |= MEM_Int;
|
||||
}else{
|
||||
pRec->r = rValue;
|
||||
pRec->flags |= MEM_Real;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,13 +311,13 @@ static void applyAffinity(
|
||||
** into a numeric representation. Use either INTEGER or REAL whichever
|
||||
** is appropriate. But only do the conversion if it is possible without
|
||||
** loss of information and return the revised type of the argument.
|
||||
**
|
||||
** This is an EXPERIMENTAL api and is subject to change or removal.
|
||||
*/
|
||||
int sqlite3_value_numeric_type(sqlite3_value *pVal){
|
||||
Mem *pMem = (Mem*)pVal;
|
||||
applyNumericAffinity(pMem);
|
||||
sqlite3VdbeMemStoreType(pMem);
|
||||
if( pMem->type==SQLITE_TEXT ){
|
||||
applyNumericAffinity(pMem);
|
||||
sqlite3VdbeMemStoreType(pMem);
|
||||
}
|
||||
return pMem->type;
|
||||
}
|
||||
|
||||
@@ -667,6 +664,7 @@ int sqlite3VdbeExec(
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
sqlite3VdbeMemReleaseExternal(pOut);
|
||||
pOut->flags = MEM_Int;
|
||||
}
|
||||
@@ -676,25 +674,30 @@ int sqlite3VdbeExec(
|
||||
if( (pOp->opflags & OPFLG_IN1)!=0 ){
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1<=p->nMem );
|
||||
assert( memIsValid(&aMem[pOp->p1]) );
|
||||
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_IN2)!=0 ){
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
assert( memIsValid(&aMem[pOp->p2]) );
|
||||
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_IN3)!=0 ){
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p3<=p->nMem );
|
||||
assert( memIsValid(&aMem[pOp->p3]) );
|
||||
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
memAboutToChange(p, &aMem[pOp->p2]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p3<=p->nMem );
|
||||
memAboutToChange(p, &aMem[pOp->p3]);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -756,6 +759,7 @@ case OP_Goto: { /* jump */
|
||||
case OP_Gosub: { /* jump, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( (pIn1->flags & MEM_Dyn)==0 );
|
||||
memAboutToChange(p, pIn1);
|
||||
pIn1->flags = MEM_Int;
|
||||
pIn1->u.i = pc;
|
||||
REGISTER_TRACE(pOp->p1, pIn1);
|
||||
@@ -961,11 +965,7 @@ case OP_Null: { /* out2-prerelease */
|
||||
/* Opcode: Blob P1 P2 * P4
|
||||
**
|
||||
** P4 points to a blob of data P1 bytes long. Store this
|
||||
** blob in register P2. This instruction is not coded directly
|
||||
** by the compiler. Instead, the compiler layer specifies
|
||||
** an OP_HexBlob opcode, with the hex string representation of
|
||||
** the blob as P4. This opcode is transformed to an OP_Blob
|
||||
** the first time it is executed.
|
||||
** blob in register P2.
|
||||
*/
|
||||
case OP_Blob: { /* out2-prerelease */
|
||||
assert( pOp->p1 <= SQLITE_MAX_LENGTH );
|
||||
@@ -1019,6 +1019,8 @@ case OP_Move: {
|
||||
while( n-- ){
|
||||
assert( pOut<=&aMem[p->nMem] );
|
||||
assert( pIn1<=&aMem[p->nMem] );
|
||||
assert( memIsValid(pIn1) );
|
||||
memAboutToChange(p, pOut);
|
||||
zMalloc = pOut->zMalloc;
|
||||
pOut->zMalloc = 0;
|
||||
sqlite3VdbeMemMove(pOut, pIn1);
|
||||
@@ -1064,6 +1066,9 @@ case OP_SCopy: { /* in1, out2 */
|
||||
pOut = &aMem[pOp->p2];
|
||||
assert( pOut!=pIn1 );
|
||||
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
|
||||
#endif
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
break;
|
||||
}
|
||||
@@ -1122,6 +1127,10 @@ case OP_ResultRow: {
|
||||
*/
|
||||
pMem = p->pResultSet = &aMem[pOp->p1];
|
||||
for(i=0; i<pOp->p2; i++){
|
||||
assert( memIsValid(&pMem[i]) );
|
||||
Deephemeralize(&pMem[i]);
|
||||
assert( (pMem[i].flags & MEM_Ephem)==0
|
||||
|| (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
|
||||
sqlite3VdbeMemNulTerminate(&pMem[i]);
|
||||
sqlite3VdbeMemStoreType(&pMem[i]);
|
||||
REGISTER_TRACE(pOp->p1+i, &pMem[i]);
|
||||
@@ -1347,12 +1356,17 @@ case OP_Function: {
|
||||
n = pOp->p5;
|
||||
apVal = p->apArg;
|
||||
assert( apVal || n==0 );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
pOut = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pOut);
|
||||
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
|
||||
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
||||
pArg = &aMem[pOp->p2];
|
||||
for(i=0; i<n; i++, pArg++){
|
||||
assert( memIsValid(pArg) );
|
||||
apVal[i] = pArg;
|
||||
Deephemeralize(pArg);
|
||||
sqlite3VdbeMemStoreType(pArg);
|
||||
REGISTER_TRACE(pOp->p2+i, pArg);
|
||||
}
|
||||
@@ -1366,8 +1380,6 @@ case OP_Function: {
|
||||
ctx.pFunc = ctx.pVdbeFunc->pFunc;
|
||||
}
|
||||
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
pOut = &aMem[pOp->p3];
|
||||
ctx.s.flags = MEM_Null;
|
||||
ctx.s.db = db;
|
||||
ctx.s.xDel = 0;
|
||||
@@ -1387,7 +1399,7 @@ case OP_Function: {
|
||||
assert( pOp[-1].opcode==OP_CollSeq );
|
||||
ctx.pColl = pOp[-1].p4.pColl;
|
||||
}
|
||||
(*ctx.pFunc->xFunc)(&ctx, n, apVal);
|
||||
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
|
||||
if( db->mallocFailed ){
|
||||
/* Even though a malloc() has failed, the implementation of the
|
||||
** user function may have called an sqlite3_result_XXX() function
|
||||
@@ -1439,7 +1451,7 @@ case OP_Function: {
|
||||
/* Opcode: ShiftLeft P1 P2 P3 * *
|
||||
**
|
||||
** Shift the integer value in register P2 to the left by the
|
||||
** number of bits specified by the integer in regiser P1.
|
||||
** number of bits specified by the integer in register P1.
|
||||
** Store the result in register P3.
|
||||
** If either input is NULL, the result is NULL.
|
||||
*/
|
||||
@@ -1487,6 +1499,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
|
||||
*/
|
||||
case OP_AddImm: { /* in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
memAboutToChange(p, pIn1);
|
||||
sqlite3VdbeMemIntegerify(pIn1);
|
||||
pIn1->u.i += pOp->p2;
|
||||
break;
|
||||
@@ -1546,6 +1559,7 @@ case OP_RealAffinity: { /* in1 */
|
||||
*/
|
||||
case OP_ToText: { /* same as TK_TO_TEXT, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
memAboutToChange(p, pIn1);
|
||||
if( pIn1->flags & MEM_Null ) break;
|
||||
assert( MEM_Str==(MEM_Blob>>3) );
|
||||
pIn1->flags |= (pIn1->flags&MEM_Blob)>>3;
|
||||
@@ -1592,16 +1606,14 @@ case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
|
||||
*/
|
||||
case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){
|
||||
sqlite3VdbeMemNumerify(pIn1);
|
||||
}
|
||||
sqlite3VdbeMemNumerify(pIn1);
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_CAST */
|
||||
|
||||
/* Opcode: ToInt P1 * * * *
|
||||
**
|
||||
** Force the value in register P1 be an integer. If
|
||||
** Force the value in register P1 to be an integer. If
|
||||
** The value is currently a real number, drop its fractional part.
|
||||
** If the value is text or blob, try to convert it to an integer using the
|
||||
** equivalent of atoi() and store 0 if no such conversion is possible.
|
||||
@@ -1628,6 +1640,7 @@ case OP_ToInt: { /* same as TK_TO_INT, in1 */
|
||||
*/
|
||||
case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
memAboutToChange(p, pIn1);
|
||||
if( (pIn1->flags & MEM_Null)==0 ){
|
||||
sqlite3VdbeMemRealify(pIn1);
|
||||
}
|
||||
@@ -1642,7 +1655,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
**
|
||||
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
|
||||
** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
|
||||
** bit is clear then fall thru if either operand is NULL.
|
||||
** bit is clear then fall through if either operand is NULL.
|
||||
**
|
||||
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
|
||||
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
|
||||
@@ -1770,6 +1783,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
pOut->u.i = res;
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
@@ -1801,8 +1815,8 @@ case OP_Permutation: {
|
||||
|
||||
/* Opcode: Compare P1 P2 P3 P4 *
|
||||
**
|
||||
** Compare to vectors of registers in reg(P1)..reg(P1+P3-1) (all this
|
||||
** one "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
|
||||
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
|
||||
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
|
||||
** the comparison for use by the next OP_Jump instruct.
|
||||
**
|
||||
** P4 is a KeyInfo structure that defines collating sequences and sort
|
||||
@@ -1842,6 +1856,8 @@ case OP_Compare: {
|
||||
#endif /* SQLITE_DEBUG */
|
||||
for(i=0; i<n; i++){
|
||||
idx = aPermute ? aPermute[i] : i;
|
||||
assert( memIsValid(&aMem[p1+idx]) );
|
||||
assert( memIsValid(&aMem[p2+idx]) );
|
||||
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
|
||||
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
|
||||
assert( i<pKeyInfo->nField );
|
||||
@@ -2067,6 +2083,7 @@ case OP_Column: {
|
||||
assert( p1<p->nCursor );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
zRec = 0;
|
||||
|
||||
@@ -2114,6 +2131,7 @@ case OP_Column: {
|
||||
}else if( pC->pseudoTableReg>0 ){
|
||||
pReg = &aMem[pC->pseudoTableReg];
|
||||
assert( pReg->flags & MEM_Blob );
|
||||
assert( memIsValid(pReg) );
|
||||
payloadSize = pReg->n;
|
||||
zRec = pReg->z;
|
||||
pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
|
||||
@@ -2336,6 +2354,7 @@ case OP_Affinity: {
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
while( (cAff = *(zAffinity++))!=0 ){
|
||||
assert( pIn1 <= &p->aMem[p->nMem] );
|
||||
assert( memIsValid(pIn1) );
|
||||
ExpandBlob(pIn1);
|
||||
applyAffinity(pIn1, cAff, encoding);
|
||||
pIn1++;
|
||||
@@ -2345,12 +2364,9 @@ case OP_Affinity: {
|
||||
|
||||
/* Opcode: MakeRecord P1 P2 P3 P4 *
|
||||
**
|
||||
** Convert P2 registers beginning with P1 into a single entry
|
||||
** suitable for use as a data record in a database table or as a key
|
||||
** in an index. The details of the format are irrelevant as long as
|
||||
** the OP_Column opcode can decode the record later.
|
||||
** Refer to source code comments for the details of the record
|
||||
** format.
|
||||
** Convert P2 registers beginning with P1 into the [record format]
|
||||
** use as a data record in a database table or as a key
|
||||
** in an index. The OP_Column opcode can decode the record later.
|
||||
**
|
||||
** P4 may be a string that is P2 characters long. The nth character of the
|
||||
** string indicates the column affinity that should be used for the nth
|
||||
@@ -2405,10 +2421,16 @@ case OP_MakeRecord: {
|
||||
pLast = &pData0[nField-1];
|
||||
file_format = p->minWriteFileFormat;
|
||||
|
||||
/* Identify the output register */
|
||||
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
|
||||
pOut = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pOut);
|
||||
|
||||
/* Loop through the elements that will make up the record to figure
|
||||
** out how much space is required for the new record.
|
||||
*/
|
||||
for(pRec=pData0; pRec<=pLast; pRec++){
|
||||
assert( memIsValid(pRec) );
|
||||
if( zAffinity ){
|
||||
applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
|
||||
}
|
||||
@@ -2443,8 +2465,6 @@ case OP_MakeRecord: {
|
||||
** be one of the input registers (because the following call to
|
||||
** sqlite3VdbeMemGrow() could clobber the value before it is used).
|
||||
*/
|
||||
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
|
||||
pOut = &aMem[pOp->p3];
|
||||
if( sqlite3VdbeMemGrow(pOut, (int)nByte, 0) ){
|
||||
goto no_mem;
|
||||
}
|
||||
@@ -2613,6 +2633,7 @@ case OP_Savepoint: {
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2991,6 +3012,8 @@ case OP_OpenWrite: {
|
||||
assert( p2>0 );
|
||||
assert( p2<=p->nMem );
|
||||
pIn2 = &aMem[p2];
|
||||
assert( memIsValid(pIn2) );
|
||||
assert( (pIn2->flags & MEM_Int)!=0 );
|
||||
sqlite3VdbeMemIntegerify(pIn2);
|
||||
p2 = (int)pIn2->u.i;
|
||||
/* The p2 value always comes from a prior OP_CreateTable opcode and
|
||||
@@ -3013,6 +3036,7 @@ case OP_OpenWrite: {
|
||||
pCur = allocateCursor(p, pOp->p1, nField, iDb, 1);
|
||||
if( pCur==0 ) goto no_mem;
|
||||
pCur->nullRow = 1;
|
||||
pCur->isOrdered = 1;
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
|
||||
pCur->pKeyInfo = pKeyInfo;
|
||||
|
||||
@@ -3063,7 +3087,7 @@ case OP_OpenWrite: {
|
||||
case OP_OpenAutoindex:
|
||||
case OP_OpenEphemeral: {
|
||||
VdbeCursor *pCx;
|
||||
static const int openFlags =
|
||||
static const int vfsFlags =
|
||||
SQLITE_OPEN_READWRITE |
|
||||
SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_EXCLUSIVE |
|
||||
@@ -3074,21 +3098,21 @@ case OP_OpenEphemeral: {
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
|
||||
&pCx->pBt);
|
||||
rc = sqlite3BtreeOpen(0, db, &pCx->pBt,
|
||||
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If a transient index is required, create it by calling
|
||||
** sqlite3BtreeCreateTable() with the BTREE_ZERODATA flag before
|
||||
** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
|
||||
** opening it. If a transient table is required, just use the
|
||||
** automatically created table with root-page 1 (an INTKEY table).
|
||||
** automatically created table with root-page 1 (an BLOB_INTKEY table).
|
||||
*/
|
||||
if( pOp->p4.pKeyInfo ){
|
||||
int pgno;
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA);
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pgno==MASTER_ROOT+1 );
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
|
||||
@@ -3102,6 +3126,7 @@ case OP_OpenEphemeral: {
|
||||
pCx->isTable = 1;
|
||||
}
|
||||
}
|
||||
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
|
||||
pCx->isIndex = !pCx->isTable;
|
||||
break;
|
||||
}
|
||||
@@ -3217,6 +3242,7 @@ case OP_SeekGt: { /* jump, in3 */
|
||||
assert( OP_SeekLe == OP_SeekLt+1 );
|
||||
assert( OP_SeekGe == OP_SeekLt+2 );
|
||||
assert( OP_SeekGt == OP_SeekLt+3 );
|
||||
assert( pC->isOrdered );
|
||||
if( pC->pCursor!=0 ){
|
||||
oc = pOp->opcode;
|
||||
pC->nullRow = 0;
|
||||
@@ -3299,6 +3325,9 @@ case OP_SeekGt: { /* jump, in3 */
|
||||
assert( oc!=OP_SeekLt || r.flags==0 );
|
||||
|
||||
r.aMem = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
ExpandBlob(r.aMem);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -3423,11 +3452,14 @@ case OP_Found: { /* jump, in3 */
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp->p4.i;
|
||||
r.aMem = pIn3;
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
r.flags = UNPACKED_PREFIX_MATCH;
|
||||
pIdxKey = &r;
|
||||
}else{
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
ExpandBlob(pIn3);
|
||||
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
|
||||
aTempRec, sizeof(aTempRec));
|
||||
if( pIdxKey==0 ){
|
||||
@@ -3520,6 +3552,9 @@ case OP_IsUnique: { /* jump, in3 */
|
||||
r.nField = nField + 1;
|
||||
r.flags = UNPACKED_PREFIX_SEARCH;
|
||||
r.aMem = aMx;
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
|
||||
/* Extract the value of R from register P3. */
|
||||
sqlite3VdbeMemIntegerify(pIn3);
|
||||
@@ -3542,7 +3577,7 @@ case OP_IsUnique: { /* jump, in3 */
|
||||
**
|
||||
** Use the content of register P3 as a integer key. If a record
|
||||
** with that key does not exist in table of P1, then jump to P2.
|
||||
** If the record does exist, then fall thru. The cursor is left
|
||||
** If the record does exist, then fall through. The cursor is left
|
||||
** pointing to the record if it exists.
|
||||
**
|
||||
** The difference between this operation and NotFound is that this
|
||||
@@ -3696,7 +3731,9 @@ case OP_NewRowid: { /* out2-prerelease */
|
||||
/* Assert that P3 is a valid memory cell. */
|
||||
assert( pOp->p3<=p->nMem );
|
||||
pMem = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pMem);
|
||||
}
|
||||
assert( memIsValid(pMem) );
|
||||
|
||||
REGISTER_TRACE(pOp->p3, pMem);
|
||||
sqlite3VdbeMemIntegerify(pMem);
|
||||
@@ -3715,29 +3752,36 @@ case OP_NewRowid: { /* out2-prerelease */
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
|
||||
}
|
||||
if( pC->useRandomRowid ){
|
||||
/* IMPLEMENTATION-OF: R-48598-02938 If the largest ROWID is equal to the
|
||||
/* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the
|
||||
** largest possible integer (9223372036854775807) then the database
|
||||
** engine starts picking candidate ROWIDs at random until it finds one
|
||||
** that is not previously used.
|
||||
*/
|
||||
** engine starts picking positive candidate ROWIDs at random until
|
||||
** it finds one that is not previously used. */
|
||||
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
|
||||
** an AUTOINCREMENT table. */
|
||||
/* on the first attempt, simply do one more than previous */
|
||||
v = db->lastRowid;
|
||||
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
|
||||
v++; /* ensure non-zero */
|
||||
cnt = 0;
|
||||
do{
|
||||
if( cnt==0 && (v&0xffffff)==v ){
|
||||
v++;
|
||||
while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v,
|
||||
0, &res))==SQLITE_OK)
|
||||
&& (res==0)
|
||||
&& (++cnt<100)){
|
||||
/* collision - try another random rowid */
|
||||
sqlite3_randomness(sizeof(v), &v);
|
||||
if( cnt<5 ){
|
||||
/* try "small" random rowids for the initial attempts */
|
||||
v &= 0xffffff;
|
||||
}else{
|
||||
sqlite3_randomness(sizeof(v), &v);
|
||||
if( cnt<5 ) v &= 0xffffff;
|
||||
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
|
||||
}
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, 0, &res);
|
||||
cnt++;
|
||||
}while( cnt<100 && rc==SQLITE_OK && res==0 );
|
||||
v++; /* ensure non-zero */
|
||||
}
|
||||
if( rc==SQLITE_OK && res==0 ){
|
||||
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
assert( v>0 ); /* EV: R-40812-03570 */
|
||||
}
|
||||
pC->rowidIsValid = 0;
|
||||
pC->deferredMoveto = 0;
|
||||
@@ -3805,6 +3849,7 @@ case OP_InsertInt: {
|
||||
|
||||
pData = &aMem[pOp->p2];
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
assert( memIsValid(pData) );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->pCursor!=0 );
|
||||
@@ -3815,6 +3860,7 @@ case OP_InsertInt: {
|
||||
if( pOp->opcode==OP_Insert ){
|
||||
pKey = &aMem[pOp->p3];
|
||||
assert( pKey->flags & MEM_Int );
|
||||
assert( memIsValid(pKey) );
|
||||
REGISTER_TRACE(pOp->p3, pKey);
|
||||
iKey = pKey->u.i;
|
||||
}else{
|
||||
@@ -3962,6 +4008,7 @@ case OP_RowData: {
|
||||
i64 n64;
|
||||
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
|
||||
/* Note that RowKey and RowData are really exactly the same instruction */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
@@ -4290,6 +4337,9 @@ case OP_IdxDelete: {
|
||||
r.nField = (u16)pOp->p3;
|
||||
r.flags = 0;
|
||||
r.aMem = &aMem[pOp->p2];
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
|
||||
if( rc==SQLITE_OK && res==0 ){
|
||||
rc = sqlite3BtreeDelete(pCrsr);
|
||||
@@ -4370,6 +4420,7 @@ case OP_IdxGE: { /* jump */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->isOrdered );
|
||||
if( ALWAYS(pC->pCursor!=0) ){
|
||||
assert( pC->deferredMoveto==0 );
|
||||
assert( pOp->p5==0 || pOp->p5==1 );
|
||||
@@ -4382,6 +4433,9 @@ case OP_IdxGE: { /* jump */
|
||||
r.flags = UNPACKED_IGNORE_ROWID;
|
||||
}
|
||||
r.aMem = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res);
|
||||
if( pOp->opcode==OP_IdxLT ){
|
||||
res = -res;
|
||||
@@ -4481,6 +4535,8 @@ case OP_Clear: {
|
||||
if( pOp->p3 ){
|
||||
p->nChange += nChange;
|
||||
if( pOp->p3>0 ){
|
||||
assert( memIsValid(&aMem[pOp->p3]) );
|
||||
memAboutToChange(p, &aMem[pOp->p3]);
|
||||
aMem[pOp->p3].u.i += nChange;
|
||||
}
|
||||
}
|
||||
@@ -4522,9 +4578,9 @@ case OP_CreateTable: { /* out2-prerelease */
|
||||
assert( pDb->pBt!=0 );
|
||||
if( pOp->opcode==OP_CreateTable ){
|
||||
/* flags = BTREE_INTKEY; */
|
||||
flags = BTREE_LEAFDATA|BTREE_INTKEY;
|
||||
flags = BTREE_INTKEY;
|
||||
}else{
|
||||
flags = BTREE_ZERODATA;
|
||||
flags = BTREE_BLOBKEY;
|
||||
}
|
||||
rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
|
||||
pOut->u.i = pgno;
|
||||
@@ -4843,6 +4899,7 @@ case OP_Program: { /* jump */
|
||||
|
||||
pProgram = pOp->p4.pProgram;
|
||||
pRt = &aMem[pOp->p3];
|
||||
assert( memIsValid(pRt) );
|
||||
assert( pProgram->nOp>0 );
|
||||
|
||||
/* If the p5 flag is clear, then recursive invocation of triggers is
|
||||
@@ -5012,6 +5069,7 @@ case OP_MemMax: { /* in2 */
|
||||
}else{
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
}
|
||||
assert( memIsValid(pIn1) );
|
||||
sqlite3VdbeMemIntegerify(pIn1);
|
||||
pIn2 = &aMem[pOp->p2];
|
||||
sqlite3VdbeMemIntegerify(pIn2);
|
||||
@@ -5096,7 +5154,9 @@ case OP_AggStep: {
|
||||
apVal = p->apArg;
|
||||
assert( apVal || n==0 );
|
||||
for(i=0; i<n; i++, pRec++){
|
||||
assert( memIsValid(pRec) );
|
||||
apVal[i] = pRec;
|
||||
memAboutToChange(p, pRec);
|
||||
sqlite3VdbeMemStoreType(pRec);
|
||||
}
|
||||
ctx.pFunc = pOp->p4.pFunc;
|
||||
@@ -5116,7 +5176,7 @@ case OP_AggStep: {
|
||||
assert( pOp[-1].opcode==OP_CollSeq );
|
||||
ctx.pColl = pOp[-1].p4.pColl;
|
||||
}
|
||||
(ctx.pFunc->xStep)(&ctx, n, apVal);
|
||||
(ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
|
||||
if( ctx.isError ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
|
||||
rc = ctx.isError;
|
||||
@@ -5491,6 +5551,7 @@ case OP_VFilter: { /* jump */
|
||||
pQuery = &aMem[pOp->p3];
|
||||
pArgc = &pQuery[1];
|
||||
pCur = p->apCsr[pOp->p1];
|
||||
assert( memIsValid(pQuery) );
|
||||
REGISTER_TRACE(pOp->p3, pQuery);
|
||||
assert( pCur->pVtabCursor );
|
||||
pVtabCursor = pCur->pVtabCursor;
|
||||
@@ -5546,6 +5607,7 @@ case OP_VColumn: {
|
||||
assert( pCur->pVtabCursor );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
if( pCur->nullRow ){
|
||||
sqlite3VdbeMemSetNull(pDest);
|
||||
break;
|
||||
@@ -5644,10 +5706,12 @@ case OP_VRename: {
|
||||
pVtab = pOp->p4.pVtab->pVtab;
|
||||
pName = &aMem[pOp->p1];
|
||||
assert( pVtab->pModule->xRename );
|
||||
assert( memIsValid(pName) );
|
||||
REGISTER_TRACE(pOp->p1, pName);
|
||||
assert( pName->flags & MEM_Str );
|
||||
rc = pVtab->pModule->xRename(pVtab, pName->z);
|
||||
importVtabErrMsg(p, pVtab);
|
||||
p->expired = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -5694,6 +5758,8 @@ case OP_VUpdate: {
|
||||
apArg = p->apArg;
|
||||
pX = &aMem[pOp->p3];
|
||||
for(i=0; i<nArg; i++){
|
||||
assert( memIsValid(pX) );
|
||||
memAboutToChange(p, pX);
|
||||
sqlite3VdbeMemStoreType(pX);
|
||||
apArg[i] = pX;
|
||||
pX++;
|
||||
@@ -5721,6 +5787,32 @@ case OP_Pagecount: { /* out2-prerelease */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
/* Opcode: MaxPgcnt P1 P2 P3 * *
|
||||
**
|
||||
** Try to set the maximum page count for database P1 to the value in P3.
|
||||
** Do not let the maximum page count fall below the current page count and
|
||||
** do not change the maximum page count value if P3==0.
|
||||
**
|
||||
** Store the maximum page count after the change in register P2.
|
||||
*/
|
||||
case OP_MaxPgcnt: { /* out2-prerelease */
|
||||
unsigned int newMax;
|
||||
Btree *pBt;
|
||||
|
||||
pBt = db->aDb[pOp->p1].pBt;
|
||||
newMax = 0;
|
||||
if( pOp->p3 ){
|
||||
newMax = sqlite3BtreeLastPage(pBt);
|
||||
if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3;
|
||||
}
|
||||
pOut->u.i = sqlite3BtreeMaxPageCount(pBt, newMax);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
/* Opcode: Trace * * * P4 *
|
||||
**
|
||||
|
||||
+68
-67
@@ -39,16 +39,14 @@ typedef unsigned char Bool;
|
||||
**
|
||||
** Every cursor that the virtual machine has open is represented by an
|
||||
** instance of the following structure.
|
||||
**
|
||||
** If the VdbeCursor.isTriggerRow flag is set it means that this cursor is
|
||||
** really a single row that represents the NEW or OLD pseudo-table of
|
||||
** a row trigger. The data for the row is stored in VdbeCursor.pData and
|
||||
** the rowid is in VdbeCursor.iKey.
|
||||
*/
|
||||
struct VdbeCursor {
|
||||
BtCursor *pCursor; /* The cursor structure of the backend */
|
||||
Btree *pBt; /* Separate file holding temporary table */
|
||||
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
|
||||
int iDb; /* Index of cursor database in db->aDb[] (or -1) */
|
||||
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
int pseudoTableReg; /* Register holding pseudotable content. */
|
||||
int nField; /* Number of fields in the header */
|
||||
Bool zeroed; /* True if zeroed out and ready for reuse */
|
||||
Bool rowidIsValid; /* True if lastRowid is valid */
|
||||
Bool atFirst; /* True if pointing to first entry */
|
||||
@@ -57,14 +55,12 @@ struct VdbeCursor {
|
||||
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
|
||||
Bool isTable; /* True if a table requiring integer keys */
|
||||
Bool isIndex; /* True if an index containing keys only - no data */
|
||||
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
||||
Btree *pBt; /* Separate file holding temporary table */
|
||||
int pseudoTableReg; /* Register holding pseudotable content. */
|
||||
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
|
||||
int nField; /* Number of fields in the header */
|
||||
i64 seqCount; /* Sequence counter */
|
||||
Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
|
||||
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
|
||||
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
|
||||
i64 seqCount; /* Sequence counter */
|
||||
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
||||
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||
** OP_IsUnique opcode on this cursor. */
|
||||
@@ -96,26 +92,34 @@ typedef struct VdbeCursor VdbeCursor;
|
||||
** restoring the state of the VM to as it was before the sub-program
|
||||
** began executing.
|
||||
**
|
||||
** Frames are stored in a linked list headed at Vdbe.pParent. Vdbe.pParent
|
||||
** is the parent of the current frame, or zero if the current frame
|
||||
** is the main Vdbe program.
|
||||
** The memory for a VdbeFrame object is allocated and managed by a memory
|
||||
** cell in the parent (calling) frame. When the memory cell is deleted or
|
||||
** overwritten, the VdbeFrame object is not freed immediately. Instead, it
|
||||
** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame
|
||||
** list is deleted when the VM is reset in VdbeHalt(). The reason for doing
|
||||
** this instead of deleting the VdbeFrame immediately is to avoid recursive
|
||||
** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the
|
||||
** child frame are released.
|
||||
**
|
||||
** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is
|
||||
** set to NULL if the currently executing frame is the main program.
|
||||
*/
|
||||
typedef struct VdbeFrame VdbeFrame;
|
||||
struct VdbeFrame {
|
||||
Vdbe *v; /* VM this frame belongs to */
|
||||
int pc; /* Program Counter */
|
||||
Op *aOp; /* Program instructions */
|
||||
int pc; /* Program Counter in parent (calling) frame */
|
||||
Op *aOp; /* Program instructions for parent frame */
|
||||
int nOp; /* Size of aOp array */
|
||||
Mem *aMem; /* Array of memory cells */
|
||||
Mem *aMem; /* Array of memory cells for parent frame */
|
||||
int nMem; /* Number of entries in aMem */
|
||||
VdbeCursor **apCsr; /* Element of Vdbe cursors */
|
||||
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
|
||||
u16 nCursor; /* Number of entries in apCsr */
|
||||
void *token; /* Copy of SubProgram.token */
|
||||
int nChildMem; /* Number of memory cells for child frame */
|
||||
int nChildCsr; /* Number of cursors for child frame */
|
||||
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
|
||||
int nChange; /* Statement changes (Vdbe.nChanges) */
|
||||
VdbeFrame *pParent; /* Parent of this frame */
|
||||
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
|
||||
};
|
||||
|
||||
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
|
||||
@@ -128,29 +132,27 @@ struct VdbeFrame {
|
||||
/*
|
||||
** Internally, the vdbe manipulates nearly all SQL values as Mem
|
||||
** structures. Each Mem struct may cache multiple representations (string,
|
||||
** integer etc.) of the same value. A value (and therefore Mem structure)
|
||||
** has the following properties:
|
||||
**
|
||||
** Each value has a manifest type. The manifest type of the value stored
|
||||
** in a Mem struct is returned by the MemType(Mem*) macro. The type is
|
||||
** one of SQLITE_NULL, SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT or
|
||||
** SQLITE_BLOB.
|
||||
** integer etc.) of the same value.
|
||||
*/
|
||||
struct Mem {
|
||||
sqlite3 *db; /* The associated database connection */
|
||||
char *z; /* String or BLOB value */
|
||||
double r; /* Real value */
|
||||
union {
|
||||
i64 i; /* Integer value. */
|
||||
i64 i; /* Integer value used when MEM_Int is set in flags */
|
||||
int nZero; /* Used when bit MEM_Zero is set in flags */
|
||||
FuncDef *pDef; /* Used only when flags==MEM_Agg */
|
||||
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
|
||||
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
|
||||
} u;
|
||||
double r; /* Real value */
|
||||
sqlite3 *db; /* The associated database connection */
|
||||
char *z; /* String or BLOB value */
|
||||
int n; /* Number of characters in string value, excluding '\0' */
|
||||
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
||||
u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
|
||||
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
|
||||
#ifdef SQLITE_DEBUG
|
||||
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
|
||||
void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
|
||||
#endif
|
||||
void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
|
||||
char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */
|
||||
};
|
||||
@@ -166,9 +168,6 @@ struct Mem {
|
||||
** database (see below for exceptions). If the MEM_Term flag is also
|
||||
** set, then the string is nul terminated. The MEM_Int and MEM_Real
|
||||
** flags may coexist with the MEM_Str flag.
|
||||
**
|
||||
** Multiple of these values can appear in Mem.flags. But only one
|
||||
** at a time can appear in Mem.type.
|
||||
*/
|
||||
#define MEM_Null 0x0001 /* Value is NULL */
|
||||
#define MEM_Str 0x0002 /* Value is a string */
|
||||
@@ -177,6 +176,7 @@ struct Mem {
|
||||
#define MEM_Blob 0x0010 /* Value is a BLOB */
|
||||
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
|
||||
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
|
||||
#define MEM_Invalid 0x0080 /* Value is undefined */
|
||||
#define MEM_TypeMask 0x00ff /* Mask of type bits */
|
||||
|
||||
/* Whenever Mem contains a valid string or blob representation, one of
|
||||
@@ -190,19 +190,25 @@ struct Mem {
|
||||
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
|
||||
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
|
||||
#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
|
||||
|
||||
#ifdef SQLITE_OMIT_INCRBLOB
|
||||
#undef MEM_Zero
|
||||
#define MEM_Zero 0x0000
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Clear any existing type flags from a Mem and replace them with f
|
||||
*/
|
||||
#define MemSetTypeFlag(p, f) \
|
||||
((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
|
||||
|
||||
/*
|
||||
** Return true if a memory cell is not marked as invalid. This macro
|
||||
** is for use inside assert() statements only.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
|
||||
#endif
|
||||
|
||||
|
||||
/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
|
||||
** additional information about auxiliary information bound to arguments
|
||||
@@ -244,23 +250,11 @@ struct sqlite3_context {
|
||||
CollSeq *pColl; /* Collating sequence */
|
||||
};
|
||||
|
||||
/*
|
||||
** A Set structure is used for quick testing to see if a value
|
||||
** is part of a small set. Sets are used to implement code like
|
||||
** this:
|
||||
** x.y IN ('hi','hoo','hum')
|
||||
*/
|
||||
typedef struct Set Set;
|
||||
struct Set {
|
||||
Hash hash; /* A set is just a hash table */
|
||||
HashElem *prev; /* Previously accessed hash elemen */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
**
|
||||
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile()
|
||||
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
|
||||
** is really a pointer to an instance of this structure.
|
||||
**
|
||||
** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
|
||||
@@ -273,31 +267,31 @@ struct Set {
|
||||
*/
|
||||
struct Vdbe {
|
||||
sqlite3 *db; /* The database connection that owns this statement */
|
||||
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
|
||||
int nOp; /* Number of instructions in the program */
|
||||
int nOpAlloc; /* Number of slots allocated for aOp[] */
|
||||
Op *aOp; /* Space to hold the virtual machine's program */
|
||||
int nLabel; /* Number of labels used */
|
||||
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
Mem *aMem; /* The memory locations */
|
||||
Mem **apArg; /* Arguments to currently executing user function */
|
||||
Mem *aColName; /* Column names to return */
|
||||
Mem *pResultSet; /* Pointer to an array of results */
|
||||
int nMem; /* Number of memory locations currently allocated */
|
||||
int nOp; /* Number of instructions in the program */
|
||||
int nOpAlloc; /* Number of slots allocated for aOp[] */
|
||||
int nLabel; /* Number of labels used */
|
||||
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
u16 nResColumn; /* Number of columns in one row of the result set */
|
||||
u16 nCursor; /* Number of slots in apCsr[] */
|
||||
u32 magic; /* Magic number for sanity checking */
|
||||
char *zErrMsg; /* Error message written here */
|
||||
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
|
||||
VdbeCursor **apCsr; /* One element of this array for each open cursor */
|
||||
u8 errorAction; /* Recovery action to do in case of an error */
|
||||
u8 okVar; /* True if azVar[] has been initialized */
|
||||
ynVar nVar; /* Number of entries in aVar[] */
|
||||
Mem *aVar; /* Values for the OP_Variable opcode. */
|
||||
char **azVar; /* Name of variables */
|
||||
u32 magic; /* Magic number for sanity checking */
|
||||
int nMem; /* Number of memory locations currently allocated */
|
||||
Mem *aMem; /* The memory locations */
|
||||
ynVar nVar; /* Number of entries in aVar[] */
|
||||
u32 cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
int pc; /* The program counter */
|
||||
int rc; /* Value to return */
|
||||
char *zErrMsg; /* Error message written here */
|
||||
u8 errorAction; /* Recovery action to do in case of an error */
|
||||
u8 okVar; /* True if azVar[] has been initialized */
|
||||
u8 explain; /* True if EXPLAIN present on SQL command */
|
||||
u8 changeCntOn; /* True to update the change-counter */
|
||||
u8 expired; /* True if the VM needs to be recompiled */
|
||||
@@ -309,18 +303,21 @@ struct Vdbe {
|
||||
u8 isPrepareV2; /* True if prepared with prepare_v2() */
|
||||
int nChange; /* Number of db changes made since last reset */
|
||||
int btreeMask; /* Bitmask of db->aDb[] entries referenced */
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
|
||||
int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
|
||||
char *zSql; /* Text of the SQL statement that generated this */
|
||||
void *pFree; /* Free this when deleting the vdbe */
|
||||
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
#endif
|
||||
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
|
||||
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
|
||||
int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
char *zSql; /* Text of the SQL statement that generated this */
|
||||
void *pFree; /* Free this when deleting the vdbe */
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
VdbeFrame *pFrame; /* Parent frame */
|
||||
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
|
||||
int nFrame; /* Number of frames in pFrame list */
|
||||
u32 expmask; /* Binding to these vars invalidates VM */
|
||||
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
|
||||
@@ -391,6 +388,10 @@ void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
void sqlite3VdbeMemStoreType(Mem *pMem);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
int sqlite3VdbeCheckFk(Vdbe *, int);
|
||||
#else
|
||||
|
||||
+51
-7
@@ -65,6 +65,8 @@ static int vdbeSafetyNotNull(Vdbe *p){
|
||||
int sqlite3_finalize(sqlite3_stmt *pStmt){
|
||||
int rc;
|
||||
if( pStmt==0 ){
|
||||
/* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL
|
||||
** pointer is a harmless no-op. */
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
@@ -141,7 +143,7 @@ const void *sqlite3_value_blob(sqlite3_value *pVal){
|
||||
sqlite3VdbeMemExpandBlob(p);
|
||||
p->flags &= ~MEM_Str;
|
||||
p->flags |= MEM_Blob;
|
||||
return p->z;
|
||||
return p->n ? p->z : 0;
|
||||
}else{
|
||||
return sqlite3_value_text(pVal);
|
||||
}
|
||||
@@ -343,11 +345,30 @@ static int sqlite3Step(Vdbe *p){
|
||||
assert(p);
|
||||
if( p->magic!=VDBE_MAGIC_RUN ){
|
||||
/* We used to require that sqlite3_reset() be called before retrying
|
||||
** sqlite3_step() after any error. But after 3.6.23, we changed this
|
||||
** so that sqlite3_reset() would be called automatically instead of
|
||||
** throwing the error.
|
||||
** sqlite3_step() after any error or after SQLITE_DONE. But beginning
|
||||
** with version 3.7.0, we changed this so that sqlite3_reset() would
|
||||
** be called automatically instead of throwing the SQLITE_MISUSE error.
|
||||
** This "automatic-reset" change is not technically an incompatibility,
|
||||
** since any application that receives an SQLITE_MISUSE is broken by
|
||||
** definition.
|
||||
**
|
||||
** Nevertheless, some published applications that were originally written
|
||||
** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
|
||||
** returns, and the so were broken by the automatic-reset change. As a
|
||||
** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
|
||||
** legacy behavior of returning SQLITE_MISUSE for cases where the
|
||||
** previous sqlite3_step() returned something other than a SQLITE_LOCKED
|
||||
** or SQLITE_BUSY error.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_AUTORESET
|
||||
if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){
|
||||
sqlite3_reset((sqlite3_stmt*)p);
|
||||
}else{
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#else
|
||||
sqlite3_reset((sqlite3_stmt*)p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check that malloc() has not failed. If it has, return early. */
|
||||
@@ -389,7 +410,9 @@ static int sqlite3Step(Vdbe *p){
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
{
|
||||
db->vdbeExecCnt++;
|
||||
rc = sqlite3VdbeExec(p);
|
||||
db->vdbeExecCnt--;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
@@ -495,6 +518,12 @@ void *sqlite3_user_data(sqlite3_context *p){
|
||||
/*
|
||||
** Extract the user data from a sqlite3_context structure and return a
|
||||
** pointer to it.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-46798-50301 The sqlite3_context_db_handle() interface
|
||||
** returns a copy of the pointer to the database connection (the 1st
|
||||
** parameter) of the sqlite3_create_function() and
|
||||
** sqlite3_create_function16() routines that originally registered the
|
||||
** application defined function.
|
||||
*/
|
||||
sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
|
||||
assert( p && p->pFunc );
|
||||
@@ -677,7 +706,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
|
||||
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
|
||||
__attribute__((aligned(8)))
|
||||
#endif
|
||||
= {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
|
||||
= {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
|
||||
|
||||
if( pVm && ALWAYS(pVm->db) ){
|
||||
sqlite3_mutex_enter(pVm->db->mutex);
|
||||
@@ -704,8 +733,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
|
||||
** sqlite3_column_real()
|
||||
** sqlite3_column_bytes()
|
||||
** sqlite3_column_bytes16()
|
||||
**
|
||||
** But not for sqlite3_column_blob(), which never calls malloc().
|
||||
** sqiite3_column_blob()
|
||||
*/
|
||||
static void columnMallocFailure(sqlite3_stmt *pStmt)
|
||||
{
|
||||
@@ -973,6 +1001,12 @@ static int vdbeUnbind(Vdbe *p, int i){
|
||||
|
||||
/* If the bit corresponding to this variable in Vdbe.expmask is set, then
|
||||
** binding a new value to this variable invalidates the current query plan.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host
|
||||
** parameter in the WHERE clause might influence the choice of query plan
|
||||
** for a statement, then the statement will be automatically recompiled,
|
||||
** as if there had been a schema change, on the first sqlite3_step() call
|
||||
** following any change to the bindings of that parameter.
|
||||
*/
|
||||
if( p->isPrepareV2 &&
|
||||
((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff)
|
||||
@@ -1009,6 +1043,8 @@ static int bindText(
|
||||
rc = sqlite3ApiExit(p->db, rc);
|
||||
}
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
}else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){
|
||||
xDel((void*)zData);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1251,6 +1287,14 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){
|
||||
return pStmt ? ((Vdbe*)pStmt)->db : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the prepared statement is guaranteed to not modify the
|
||||
** database.
|
||||
*/
|
||||
int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
|
||||
return pStmt ? ((Vdbe*)pStmt)->readOnly : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the next prepared statement after pStmt associated
|
||||
** with database connection pDb. If pStmt is NULL, return the first
|
||||
|
||||
+13
-9
@@ -408,7 +408,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
pOp->opflags = sqlite3OpcodeProperty[opcode];
|
||||
if( opcode==OP_Function || opcode==OP_AggStep ){
|
||||
if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
|
||||
}else if( opcode==OP_Transaction && pOp->p2!=0 ){
|
||||
}else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
|
||||
p->readOnly = 0;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
}else if( opcode==OP_VUpdate ){
|
||||
@@ -1182,12 +1182,10 @@ int sqlite3VdbeList(
|
||||
pMem->type = SQLITE_INTEGER;
|
||||
pMem++;
|
||||
|
||||
if( p->explain==1 ){
|
||||
pMem->flags = MEM_Int;
|
||||
pMem->u.i = pOp->p3; /* P3 */
|
||||
pMem->type = SQLITE_INTEGER;
|
||||
pMem++;
|
||||
}
|
||||
pMem->flags = MEM_Int;
|
||||
pMem->u.i = pOp->p3; /* P3 */
|
||||
pMem->type = SQLITE_INTEGER;
|
||||
pMem++;
|
||||
|
||||
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
|
||||
assert( p->db->mallocFailed );
|
||||
@@ -1232,7 +1230,7 @@ int sqlite3VdbeList(
|
||||
}
|
||||
}
|
||||
|
||||
p->nResColumn = 8 - 5*(p->explain-1);
|
||||
p->nResColumn = 8 - 4*(p->explain-1);
|
||||
p->rc = SQLITE_OK;
|
||||
rc = SQLITE_ROW;
|
||||
}
|
||||
@@ -1539,6 +1537,11 @@ static void closeAllCursors(Vdbe *p){
|
||||
if( p->aMem ){
|
||||
releaseMemArray(&p->aMem[1], p->nMem);
|
||||
}
|
||||
while( p->pDelFrame ){
|
||||
VdbeFrame *pDel = p->pDelFrame;
|
||||
p->pDelFrame = pDel->pParent;
|
||||
sqlite3VdbeFrameDelete(pDel);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1755,9 +1758,10 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( sqlite3BtreeIsInTrans(pBt) ){
|
||||
char const *zFile = sqlite3BtreeGetJournalname(pBt);
|
||||
if( zFile==0 || zFile[0]==0 ){
|
||||
if( zFile==0 ){
|
||||
continue; /* Ignore TEMP and :memory: databases */
|
||||
}
|
||||
assert( zFile[0]!=0 );
|
||||
if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
|
||||
needSync = 1;
|
||||
}
|
||||
|
||||
+147
-67
@@ -26,11 +26,82 @@ struct Incrblob {
|
||||
int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
|
||||
int nByte; /* Size of open blob, in bytes */
|
||||
int iOffset; /* Byte offset of blob in cursor data */
|
||||
int iCol; /* Table column this handle is open on */
|
||||
BtCursor *pCsr; /* Cursor pointing at blob row */
|
||||
sqlite3_stmt *pStmt; /* Statement holding cursor open */
|
||||
sqlite3 *db; /* The associated database */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** This function is used by both blob_open() and blob_reopen(). It seeks
|
||||
** the b-tree cursor associated with blob handle p to point to row iRow.
|
||||
** If successful, SQLITE_OK is returned and subsequent calls to
|
||||
** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.
|
||||
**
|
||||
** If an error occurs, or if the specified row does not exist or does not
|
||||
** contain a value of type TEXT or BLOB in the column nominated when the
|
||||
** blob handle was opened, then an error code is returned and *pzErr may
|
||||
** be set to point to a buffer containing an error message. It is the
|
||||
** responsibility of the caller to free the error message buffer using
|
||||
** sqlite3DbFree().
|
||||
**
|
||||
** If an error does occur, then the b-tree cursor is closed. All subsequent
|
||||
** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
|
||||
** immediately return SQLITE_ABORT.
|
||||
*/
|
||||
static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
|
||||
int rc; /* Error code */
|
||||
char *zErr = 0; /* Error message */
|
||||
Vdbe *v = (Vdbe *)p->pStmt;
|
||||
|
||||
/* Set the value of the SQL statements only variable to integer iRow.
|
||||
** This is done directly instead of using sqlite3_bind_int64() to avoid
|
||||
** triggering asserts related to mutexes.
|
||||
*/
|
||||
assert( v->aVar[0].flags&MEM_Int );
|
||||
v->aVar[0].u.i = iRow;
|
||||
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
u32 type = v->apCsr[0]->aType[p->iCol];
|
||||
if( type<12 ){
|
||||
zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
|
||||
type==0?"null": type==7?"real": "integer"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}else{
|
||||
p->iOffset = v->apCsr[0]->aOffset[p->iCol];
|
||||
p->nByte = sqlite3VdbeSerialTypeLen(type);
|
||||
p->pCsr = v->apCsr[0]->pCursor;
|
||||
sqlite3BtreeEnterCursor(p->pCsr);
|
||||
sqlite3BtreeCacheOverflow(p->pCsr);
|
||||
sqlite3BtreeLeaveCursor(p->pCsr);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_ROW ){
|
||||
rc = SQLITE_OK;
|
||||
}else if( p->pStmt ){
|
||||
rc = sqlite3_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));
|
||||
}
|
||||
}
|
||||
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );
|
||||
|
||||
*pzErr = zErr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a blob handle.
|
||||
*/
|
||||
@@ -71,29 +142,35 @@ int sqlite3_blob_open(
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
|
||||
|
||||
{OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */
|
||||
{OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 7 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 8 */
|
||||
{OP_Close, 0, 0, 0}, /* 9 */
|
||||
{OP_Halt, 0, 0, 0}, /* 10 */
|
||||
{OP_Goto, 0, 5, 0}, /* 9 */
|
||||
{OP_Close, 0, 0, 0}, /* 10 */
|
||||
{OP_Halt, 0, 0, 0}, /* 11 */
|
||||
};
|
||||
|
||||
Vdbe *v = 0;
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
Table *pTab;
|
||||
Parse *pParse;
|
||||
Parse *pParse = 0;
|
||||
Incrblob *pBlob = 0;
|
||||
|
||||
flags = !!flags; /* flags = (flags ? 1 : 0); */
|
||||
*ppBlob = 0;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto blob_open_out;
|
||||
}
|
||||
if( !pParse ) goto blob_open_out;
|
||||
|
||||
do {
|
||||
memset(pParse, 0, sizeof(Parse));
|
||||
pParse->db = db;
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = 0;
|
||||
|
||||
sqlite3BtreeEnterAll(db);
|
||||
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
|
||||
@@ -119,7 +196,7 @@ int sqlite3_blob_open(
|
||||
}
|
||||
|
||||
/* Now search pTab for the exact column. */
|
||||
for(iCol=0; iCol < pTab->nCol; iCol++) {
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++) {
|
||||
if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
|
||||
break;
|
||||
}
|
||||
@@ -173,11 +250,14 @@ int sqlite3_blob_open(
|
||||
}
|
||||
}
|
||||
|
||||
v = sqlite3VdbeCreate(db);
|
||||
if( v ){
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
|
||||
assert( pBlob->pStmt || db->mallocFailed );
|
||||
if( pBlob->pStmt ){
|
||||
Vdbe *v = (Vdbe *)pBlob->pStmt;
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
||||
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
|
||||
flags = !!flags; /* flags = (flags ? 1 : 0); */
|
||||
|
||||
|
||||
/* Configure the OP_Transaction */
|
||||
sqlite3VdbeChangeP1(v, 0, iDb);
|
||||
@@ -220,65 +300,25 @@ int sqlite3_blob_open(
|
||||
}
|
||||
}
|
||||
|
||||
pBlob->flags = flags;
|
||||
pBlob->iCol = iCol;
|
||||
pBlob->db = db;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( db->mallocFailed ){
|
||||
goto blob_open_out;
|
||||
}
|
||||
|
||||
sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
|
||||
rc = sqlite3_step((sqlite3_stmt *)v);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
nAttempt++;
|
||||
rc = sqlite3_finalize((sqlite3_stmt *)v);
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = sqlite3MPrintf(db, sqlite3_errmsg(db));
|
||||
v = 0;
|
||||
}
|
||||
} while( nAttempt<5 && rc==SQLITE_SCHEMA );
|
||||
|
||||
if( rc==SQLITE_ROW ){
|
||||
/* The row-record has been opened successfully. Check that the
|
||||
** column in question contains text or a blob. If it contains
|
||||
** text, it is up to the caller to get the encoding right.
|
||||
*/
|
||||
Incrblob *pBlob;
|
||||
u32 type = v->apCsr[0]->aType[iCol];
|
||||
|
||||
if( type<12 ){
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = sqlite3MPrintf(db, "cannot open value of type %s",
|
||||
type==0?"null": type==7?"real": "integer"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
goto blob_open_out;
|
||||
}
|
||||
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
||||
if( db->mallocFailed ){
|
||||
sqlite3DbFree(db, pBlob);
|
||||
goto blob_open_out;
|
||||
}
|
||||
pBlob->flags = flags;
|
||||
pBlob->pCsr = v->apCsr[0]->pCursor;
|
||||
sqlite3BtreeEnterCursor(pBlob->pCsr);
|
||||
sqlite3BtreeCacheOverflow(pBlob->pCsr);
|
||||
sqlite3BtreeLeaveCursor(pBlob->pCsr);
|
||||
pBlob->pStmt = (sqlite3_stmt *)v;
|
||||
pBlob->iOffset = v->apCsr[0]->aOffset[iCol];
|
||||
pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
|
||||
pBlob->db = db;
|
||||
*ppBlob = (sqlite3_blob *)pBlob;
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK ){
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = sqlite3MPrintf(db, "no such rowid: %lld", iRow);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
|
||||
rc = blobSeekToRow(pBlob, iRow, &zErr);
|
||||
} while( (++nAttempt)<5 && rc==SQLITE_SCHEMA );
|
||||
|
||||
blob_open_out:
|
||||
if( v && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(v);
|
||||
if( rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
*ppBlob = (sqlite3_blob *)pBlob;
|
||||
}else{
|
||||
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
|
||||
sqlite3DbFree(db, pBlob);
|
||||
}
|
||||
sqlite3Error(db, rc, zErr);
|
||||
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
sqlite3StackFree(db, pParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
@@ -331,7 +371,7 @@ static int blobReadWrite(
|
||||
/* Request is out of range. Return a transient error. */
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3Error(db, SQLITE_ERROR, 0);
|
||||
} else if( v==0 ){
|
||||
}else if( v==0 ){
|
||||
/* If there is no statement handle, then the blob-handle has
|
||||
** already been invalidated. Return SQLITE_ABORT in this case.
|
||||
*/
|
||||
@@ -379,7 +419,47 @@ int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
|
||||
*/
|
||||
int sqlite3_blob_bytes(sqlite3_blob *pBlob){
|
||||
Incrblob *p = (Incrblob *)pBlob;
|
||||
return p ? p->nByte : 0;
|
||||
return (p && p->pStmt) ? p->nByte : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Move an existing blob handle to point to a different row of the same
|
||||
** database table.
|
||||
**
|
||||
** If an error occurs, or if the specified row does not exist or does not
|
||||
** contain a blob or text value, then an error code is returned and the
|
||||
** database handle error code and message set. If this happens, then all
|
||||
** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
|
||||
** immediately return SQLITE_ABORT.
|
||||
*/
|
||||
int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
|
||||
int rc;
|
||||
Incrblob *p = (Incrblob *)pBlob;
|
||||
sqlite3 *db;
|
||||
|
||||
if( p==0 ) return SQLITE_MISUSE_BKPT;
|
||||
db = p->db;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
if( p->pStmt==0 ){
|
||||
/* If there is no statement handle, then the blob-handle has
|
||||
** already been invalidated. Return SQLITE_ABORT in this case.
|
||||
*/
|
||||
rc = SQLITE_ABORT;
|
||||
}else{
|
||||
char *zErr;
|
||||
rc = blobSeekToRow(p, iRow, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
}
|
||||
assert( rc!=SQLITE_SCHEMA );
|
||||
}
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( rc==SQLITE_OK || p->pStmt==0 );
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
|
||||
|
||||
+65
-34
@@ -132,6 +132,9 @@ int sqlite3VdbeMemMakeWriteable(Mem *pMem){
|
||||
pMem->z[pMem->n] = 0;
|
||||
pMem->z[pMem->n+1] = 0;
|
||||
pMem->flags |= MEM_Term;
|
||||
#ifdef SQLITE_DEBUG
|
||||
pMem->pScopyFrom = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
@@ -252,7 +255,7 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
ctx.s.db = pMem->db;
|
||||
ctx.pMem = pMem;
|
||||
ctx.pFunc = pFunc;
|
||||
pFunc->xFinalize(&ctx);
|
||||
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
|
||||
assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
|
||||
sqlite3DbFree(pMem->db, pMem->zMalloc);
|
||||
memcpy(pMem, &ctx.s, sizeof(ctx.s));
|
||||
@@ -365,13 +368,9 @@ i64 sqlite3VdbeIntValue(Mem *pMem){
|
||||
return doubleToInt64(pMem->r);
|
||||
}else if( flags & (MEM_Str|MEM_Blob) ){
|
||||
i64 value;
|
||||
pMem->flags |= MEM_Str;
|
||||
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|
||||
|| sqlite3VdbeMemNulTerminate(pMem) ){
|
||||
return 0;
|
||||
}
|
||||
assert( pMem->z );
|
||||
sqlite3Atoi64(pMem->z, &value);
|
||||
assert( pMem->z || pMem->n==0 );
|
||||
testcase( pMem->z==0 );
|
||||
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
|
||||
return value;
|
||||
}else{
|
||||
return 0;
|
||||
@@ -394,14 +393,7 @@ double sqlite3VdbeRealValue(Mem *pMem){
|
||||
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
|
||||
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
double val = (double)0;
|
||||
pMem->flags |= MEM_Str;
|
||||
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|
||||
|| sqlite3VdbeMemNulTerminate(pMem) ){
|
||||
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
return (double)0;
|
||||
}
|
||||
assert( pMem->z );
|
||||
sqlite3AtoF(pMem->z, &val);
|
||||
sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc);
|
||||
return val;
|
||||
}else{
|
||||
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
@@ -474,21 +466,19 @@ int sqlite3VdbeMemRealify(Mem *pMem){
|
||||
** as much of the string as we can and ignore the rest.
|
||||
*/
|
||||
int sqlite3VdbeMemNumerify(Mem *pMem){
|
||||
int rc;
|
||||
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 );
|
||||
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
rc = sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3VdbeMemNulTerminate(pMem);
|
||||
if( rc ) return rc;
|
||||
if( sqlite3Atoi64(pMem->z, &pMem->u.i) ){
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
}else{
|
||||
pMem->r = sqlite3VdbeRealValue(pMem);
|
||||
MemSetTypeFlag(pMem, MEM_Real);
|
||||
sqlite3VdbeIntegerAffinity(pMem);
|
||||
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
|
||||
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
}else{
|
||||
pMem->r = sqlite3VdbeRealValue(pMem);
|
||||
MemSetTypeFlag(pMem, MEM_Real);
|
||||
sqlite3VdbeIntegerAffinity(pMem);
|
||||
}
|
||||
}
|
||||
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
|
||||
pMem->flags &= ~(MEM_Str|MEM_Blob);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -497,7 +487,9 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
|
||||
*/
|
||||
void sqlite3VdbeMemSetNull(Mem *pMem){
|
||||
if( pMem->flags & MEM_Frame ){
|
||||
sqlite3VdbeFrameDelete(pMem->u.pFrame);
|
||||
VdbeFrame *pFrame = pMem->u.pFrame;
|
||||
pFrame->pParent = pFrame->v->pDelFrame;
|
||||
pFrame->v->pDelFrame = pFrame;
|
||||
}
|
||||
if( pMem->flags & MEM_RowSet ){
|
||||
sqlite3RowSetClear(pMem->u.pRowSet);
|
||||
@@ -593,6 +585,28 @@ int sqlite3VdbeMemTooBig(Mem *p){
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** This routine prepares a memory cell for modication by breaking
|
||||
** its link to a shallow copy and by marking any current shallow
|
||||
** copies of this cell as invalid.
|
||||
**
|
||||
** This is used for testing and debugging only - to make sure shallow
|
||||
** copies are not misused.
|
||||
*/
|
||||
void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
int i;
|
||||
Mem *pX;
|
||||
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
|
||||
if( pX->pScopyFrom==pMem ){
|
||||
pX->flags |= MEM_Invalid;
|
||||
pX->pScopyFrom = 0;
|
||||
}
|
||||
}
|
||||
pMem->pScopyFrom = 0;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Size of struct Mem not including the Mem.zMalloc member.
|
||||
*/
|
||||
@@ -961,7 +975,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeMemNulTerminate(pVal);
|
||||
sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */
|
||||
}else{
|
||||
assert( (pVal->flags&MEM_Blob)==0 );
|
||||
sqlite3VdbeMemStringify(pVal, enc);
|
||||
@@ -1009,6 +1023,8 @@ int sqlite3ValueFromExpr(
|
||||
int op;
|
||||
char *zVal = 0;
|
||||
sqlite3_value *pVal = 0;
|
||||
int negInt = 1;
|
||||
const char *zNeg = "";
|
||||
|
||||
if( !pExpr ){
|
||||
*ppVal = 0;
|
||||
@@ -1026,13 +1042,24 @@ int sqlite3ValueFromExpr(
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
#endif
|
||||
|
||||
/* Handle negative integers in a single step. This is needed in the
|
||||
** case when the value is -9223372036854775808.
|
||||
*/
|
||||
if( op==TK_UMINUS
|
||||
&& (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
|
||||
pExpr = pExpr->pLeft;
|
||||
op = pExpr->op;
|
||||
negInt = -1;
|
||||
zNeg = "-";
|
||||
}
|
||||
|
||||
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
|
||||
pVal = sqlite3ValueNew(db);
|
||||
if( pVal==0 ) goto no_mem;
|
||||
if( ExprHasProperty(pExpr, EP_IntValue) ){
|
||||
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue);
|
||||
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
|
||||
}else{
|
||||
zVal = sqlite3DbStrDup(db, pExpr->u.zToken);
|
||||
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
|
||||
if( zVal==0 ) goto no_mem;
|
||||
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
|
||||
if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
|
||||
@@ -1042,14 +1069,18 @@ int sqlite3ValueFromExpr(
|
||||
}else{
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
|
||||
}
|
||||
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
sqlite3VdbeChangeEncoding(pVal, enc);
|
||||
}
|
||||
}else if( op==TK_UMINUS ) {
|
||||
/* This branch happens for multiple negative signs. Ex: -(-5) */
|
||||
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
|
||||
sqlite3VdbeMemNumerify(pVal);
|
||||
pVal->u.i = -1 * pVal->u.i;
|
||||
/* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
pVal->r = (double)-1 * pVal->r;
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, enc);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
||||
|
||||
+68
-56
@@ -44,9 +44,12 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which
|
||||
** holds a copy of zRawSql but with host parameters expanded to their
|
||||
** current bindings.
|
||||
** This function returns a pointer to a nul-terminated string in memory
|
||||
** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
|
||||
** string contains a copy of zRawSql but with host parameters expanded to
|
||||
** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
|
||||
** then the returned string holds a copy of zRawSql with "-- " prepended
|
||||
** to each line of text.
|
||||
**
|
||||
** The calling function is responsible for making sure the memory returned
|
||||
** is eventually freed.
|
||||
@@ -77,63 +80,72 @@ char *sqlite3VdbeExpandSql(
|
||||
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
out.db = db;
|
||||
while( zRawSql[0] ){
|
||||
n = findNextHostParameter(zRawSql, &nToken);
|
||||
assert( n>0 );
|
||||
sqlite3StrAccumAppend(&out, zRawSql, n);
|
||||
zRawSql += n;
|
||||
assert( zRawSql[0] || nToken==0 );
|
||||
if( nToken==0 ) break;
|
||||
if( zRawSql[0]=='?' ){
|
||||
if( nToken>1 ){
|
||||
assert( sqlite3Isdigit(zRawSql[1]) );
|
||||
sqlite3GetInt32(&zRawSql[1], &idx);
|
||||
}else{
|
||||
idx = nextIndex;
|
||||
}
|
||||
}else{
|
||||
assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
|
||||
testcase( zRawSql[0]==':' );
|
||||
testcase( zRawSql[0]=='$' );
|
||||
testcase( zRawSql[0]=='@' );
|
||||
idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
|
||||
assert( idx>0 );
|
||||
if( db->vdbeExecCnt>1 ){
|
||||
while( *zRawSql ){
|
||||
const char *zStart = zRawSql;
|
||||
while( *(zRawSql++)!='\n' && *zRawSql );
|
||||
sqlite3StrAccumAppend(&out, "-- ", 3);
|
||||
sqlite3StrAccumAppend(&out, zStart, zRawSql-zStart);
|
||||
}
|
||||
zRawSql += nToken;
|
||||
nextIndex = idx + 1;
|
||||
assert( idx>0 && idx<=p->nVar );
|
||||
pVar = &p->aVar[idx-1];
|
||||
if( pVar->flags & MEM_Null ){
|
||||
sqlite3StrAccumAppend(&out, "NULL", 4);
|
||||
}else if( pVar->flags & MEM_Int ){
|
||||
sqlite3XPrintf(&out, "%lld", pVar->u.i);
|
||||
}else if( pVar->flags & MEM_Real ){
|
||||
sqlite3XPrintf(&out, "%!.15g", pVar->r);
|
||||
}else if( pVar->flags & MEM_Str ){
|
||||
}else{
|
||||
while( zRawSql[0] ){
|
||||
n = findNextHostParameter(zRawSql, &nToken);
|
||||
assert( n>0 );
|
||||
sqlite3StrAccumAppend(&out, zRawSql, n);
|
||||
zRawSql += n;
|
||||
assert( zRawSql[0] || nToken==0 );
|
||||
if( nToken==0 ) break;
|
||||
if( zRawSql[0]=='?' ){
|
||||
if( nToken>1 ){
|
||||
assert( sqlite3Isdigit(zRawSql[1]) );
|
||||
sqlite3GetInt32(&zRawSql[1], &idx);
|
||||
}else{
|
||||
idx = nextIndex;
|
||||
}
|
||||
}else{
|
||||
assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
|
||||
testcase( zRawSql[0]==':' );
|
||||
testcase( zRawSql[0]=='$' );
|
||||
testcase( zRawSql[0]=='@' );
|
||||
idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
|
||||
assert( idx>0 );
|
||||
}
|
||||
zRawSql += nToken;
|
||||
nextIndex = idx + 1;
|
||||
assert( idx>0 && idx<=p->nVar );
|
||||
pVar = &p->aVar[idx-1];
|
||||
if( pVar->flags & MEM_Null ){
|
||||
sqlite3StrAccumAppend(&out, "NULL", 4);
|
||||
}else if( pVar->flags & MEM_Int ){
|
||||
sqlite3XPrintf(&out, "%lld", pVar->u.i);
|
||||
}else if( pVar->flags & MEM_Real ){
|
||||
sqlite3XPrintf(&out, "%!.15g", pVar->r);
|
||||
}else if( pVar->flags & MEM_Str ){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
u8 enc = ENC(db);
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
Mem utf8;
|
||||
memset(&utf8, 0, sizeof(utf8));
|
||||
utf8.db = db;
|
||||
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
||||
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
|
||||
sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
|
||||
sqlite3VdbeMemRelease(&utf8);
|
||||
}else
|
||||
u8 enc = ENC(db);
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
Mem utf8;
|
||||
memset(&utf8, 0, sizeof(utf8));
|
||||
utf8.db = db;
|
||||
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
||||
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
|
||||
sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
|
||||
sqlite3VdbeMemRelease(&utf8);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
|
||||
{
|
||||
sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
|
||||
}
|
||||
}else if( pVar->flags & MEM_Zero ){
|
||||
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
}else{
|
||||
assert( pVar->flags & MEM_Blob );
|
||||
sqlite3StrAccumAppend(&out, "x'", 2);
|
||||
for(i=0; i<pVar->n; i++){
|
||||
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
|
||||
}
|
||||
sqlite3StrAccumAppend(&out, "'", 1);
|
||||
}
|
||||
}else if( pVar->flags & MEM_Zero ){
|
||||
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
}else{
|
||||
assert( pVar->flags & MEM_Blob );
|
||||
sqlite3StrAccumAppend(&out, "x'", 2);
|
||||
for(i=0; i<pVar->n; i++){
|
||||
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
|
||||
}
|
||||
sqlite3StrAccumAppend(&out, "'", 1);
|
||||
}
|
||||
}
|
||||
return sqlite3StrAccumFinish(&out);
|
||||
|
||||
+2
-2
@@ -371,7 +371,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
|
||||
zWhere = sqlite3MPrintf(db, "name='%q'", pTab->zName);
|
||||
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
|
||||
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
|
||||
@@ -672,7 +672,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
}
|
||||
db->pVTab = 0;
|
||||
}else{
|
||||
sqlite3Error(db, SQLITE_ERROR, zErr);
|
||||
sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -428,6 +428,13 @@ struct Wal {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Candidate values for Wal.exclusiveMode.
|
||||
*/
|
||||
#define WAL_NORMAL_MODE 0
|
||||
#define WAL_EXCLUSIVE_MODE 1
|
||||
#define WAL_HEAPMEMORY_MODE 2
|
||||
|
||||
/*
|
||||
** Each page of the wal-index mapping contains a hash-table made up of
|
||||
** an array of HASHTABLE_NSLOT elements of the following type.
|
||||
@@ -451,14 +458,14 @@ typedef u16 ht_slot;
|
||||
*/
|
||||
struct WalIterator {
|
||||
int iPrior; /* Last result returned from the iterator */
|
||||
int nSegment; /* Size of the aSegment[] array */
|
||||
int nSegment; /* Number of entries in aSegment[] */
|
||||
struct WalSegment {
|
||||
int iNext; /* Next slot in aIndex[] not yet returned */
|
||||
ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
|
||||
u32 *aPgno; /* Array of page numbers. */
|
||||
int nEntry; /* Max size of aPgno[] and aIndex[] arrays */
|
||||
int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
|
||||
int iZero; /* Frame number associated with aPgno[0] */
|
||||
} aSegment[1]; /* One for every 32KB page in the WAL */
|
||||
} aSegment[1]; /* One for every 32KB page in the wal-index */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -514,9 +521,14 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
|
||||
|
||||
/* Request a pointer to the required page from the VFS */
|
||||
if( pWal->apWiData[iPage]==0 ){
|
||||
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
||||
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
||||
);
|
||||
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
||||
pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
|
||||
if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
||||
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*ppPage = pWal->apWiData[iPage];
|
||||
@@ -599,6 +611,12 @@ static void walChecksumBytes(
|
||||
aOut[1] = s2;
|
||||
}
|
||||
|
||||
static void walShmBarrier(Wal *pWal){
|
||||
if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
|
||||
sqlite3OsShmBarrier(pWal->pDbFd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Write the header information in pWal->hdr into the wal-index.
|
||||
**
|
||||
@@ -613,7 +631,7 @@ static void walIndexWriteHdr(Wal *pWal){
|
||||
pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
|
||||
walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
|
||||
memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
|
||||
sqlite3OsShmBarrier(pWal->pDbFd);
|
||||
walShmBarrier(pWal);
|
||||
memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
|
||||
}
|
||||
|
||||
@@ -1185,7 +1203,15 @@ recovery_error:
|
||||
** Close an open wal-index.
|
||||
*/
|
||||
static void walIndexClose(Wal *pWal, int isDelete){
|
||||
sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
|
||||
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
||||
int i;
|
||||
for(i=0; i<pWal->nWiData; i++){
|
||||
sqlite3_free((void *)pWal->apWiData[i]);
|
||||
pWal->apWiData[i] = 0;
|
||||
}
|
||||
}else{
|
||||
sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1207,6 +1233,7 @@ int sqlite3WalOpen(
|
||||
sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */
|
||||
sqlite3_file *pDbFd, /* The open database file */
|
||||
const char *zWalName, /* Name of the WAL file */
|
||||
int bNoShm, /* True to run in heap-memory mode */
|
||||
Wal **ppWal /* OUT: Allocated Wal handle */
|
||||
){
|
||||
int rc; /* Return Code */
|
||||
@@ -1240,6 +1267,7 @@ int sqlite3WalOpen(
|
||||
pRet->pDbFd = pDbFd;
|
||||
pRet->readLock = -1;
|
||||
pRet->zWalName = zWalName;
|
||||
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
|
||||
|
||||
/* Open file handle on the write-ahead log file. */
|
||||
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
|
||||
@@ -1301,9 +1329,29 @@ static int walIteratorNext(
|
||||
|
||||
/*
|
||||
** This function merges two sorted lists into a single sorted list.
|
||||
**
|
||||
** aLeft[] and aRight[] are arrays of indices. The sort key is
|
||||
** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following
|
||||
** is guaranteed for all J<K:
|
||||
**
|
||||
** aContent[aLeft[J]] < aContent[aLeft[K]]
|
||||
** aContent[aRight[J]] < aContent[aRight[K]]
|
||||
**
|
||||
** This routine overwrites aRight[] with a new (probably longer) sequence
|
||||
** of indices such that the aRight[] contains every index that appears in
|
||||
** either aLeft[] or the old aRight[] and such that the second condition
|
||||
** above is still met.
|
||||
**
|
||||
** The aContent[aLeft[X]] values will be unique for all X. And the
|
||||
** aContent[aRight[X]] values will be unique too. But there might be
|
||||
** one or more combinations of X and Y such that
|
||||
**
|
||||
** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]]
|
||||
**
|
||||
** When that happens, omit the aLeft[X] and use the aRight[Y] index.
|
||||
*/
|
||||
static void walMerge(
|
||||
u32 *aContent, /* Pages in wal */
|
||||
const u32 *aContent, /* Pages in wal - keys for the sort */
|
||||
ht_slot *aLeft, /* IN: Left hand input list */
|
||||
int nLeft, /* IN: Elements in array *paLeft */
|
||||
ht_slot **paRight, /* IN/OUT: Right hand input list */
|
||||
@@ -1343,10 +1391,24 @@ static void walMerge(
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort the elements in list aList, removing any duplicates.
|
||||
** Sort the elements in list aList using aContent[] as the sort key.
|
||||
** Remove elements with duplicate keys, preferring to keep the
|
||||
** larger aList[] values.
|
||||
**
|
||||
** The aList[] entries are indices into aContent[]. The values in
|
||||
** aList[] are to be sorted so that for all J<K:
|
||||
**
|
||||
** aContent[aList[J]] < aContent[aList[K]]
|
||||
**
|
||||
** For any X and Y such that
|
||||
**
|
||||
** aContent[aList[X]] == aContent[aList[Y]]
|
||||
**
|
||||
** Keep the larger of the two values aList[X] and aList[Y] and discard
|
||||
** the smaller.
|
||||
*/
|
||||
static void walMergesort(
|
||||
u32 *aContent, /* Pages in wal */
|
||||
const u32 *aContent, /* Pages in wal */
|
||||
ht_slot *aBuffer, /* Buffer of at least *pnList items to use */
|
||||
ht_slot *aList, /* IN/OUT: List to sort */
|
||||
int *pnList /* IN/OUT: Number of elements in aList[] */
|
||||
@@ -1411,6 +1473,7 @@ static void walIteratorFree(WalIterator *p){
|
||||
/*
|
||||
** Construct a WalInterator object that can be used to loop over all
|
||||
** pages in the WAL in ascending order. The caller must hold the checkpoint
|
||||
** lock.
|
||||
**
|
||||
** On success, make *pp point to the newly allocated WalInterator object
|
||||
** return SQLITE_OK. Otherwise, return an error code. If this routine
|
||||
@@ -1545,7 +1608,8 @@ static int walCheckpoint(
|
||||
szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
|
||||
testcase( szPage<=32768 );
|
||||
testcase( szPage>=65536 );
|
||||
if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
|
||||
pInfo = walCkptInfo(pWal);
|
||||
if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
|
||||
|
||||
/* Allocate the iterator */
|
||||
rc = walIteratorInit(pWal, &pIter);
|
||||
@@ -1567,7 +1631,6 @@ static int walCheckpoint(
|
||||
*/
|
||||
mxSafeFrame = pWal->hdr.mxFrame;
|
||||
mxPage = pWal->hdr.nPage;
|
||||
pInfo = walCkptInfo(pWal);
|
||||
for(i=1; i<WAL_NREADER; i++){
|
||||
u32 y = pInfo->aReadMark[i];
|
||||
if( mxSafeFrame>=y ){
|
||||
@@ -1673,7 +1736,9 @@ int sqlite3WalClose(
|
||||
*/
|
||||
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
|
||||
if( rc==SQLITE_OK ){
|
||||
pWal->exclusiveMode = 1;
|
||||
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
|
||||
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
||||
}
|
||||
rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf);
|
||||
if( rc==SQLITE_OK ){
|
||||
isDelete = 1;
|
||||
@@ -1729,7 +1794,7 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
|
||||
*/
|
||||
aHdr = walIndexHdr(pWal);
|
||||
memcpy(&h1, (void *)&aHdr[0], sizeof(h1));
|
||||
sqlite3OsShmBarrier(pWal->pDbFd);
|
||||
walShmBarrier(pWal);
|
||||
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
|
||||
|
||||
if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
|
||||
@@ -1930,7 +1995,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
** and can be safely ignored.
|
||||
*/
|
||||
rc = walLockShared(pWal, WAL_READ_LOCK(0));
|
||||
sqlite3OsShmBarrier(pWal->pDbFd);
|
||||
walShmBarrier(pWal);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
|
||||
/* It is not safe to allow the reader to continue here if frames
|
||||
@@ -2024,7 +2089,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
** log-wrap (either of which would require an exclusive lock on
|
||||
** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
|
||||
*/
|
||||
sqlite3OsShmBarrier(pWal->pDbFd);
|
||||
walShmBarrier(pWal);
|
||||
if( pInfo->aReadMark[mxI]!=mxReadMark
|
||||
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
|
||||
){
|
||||
@@ -2366,7 +2431,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
|
||||
**
|
||||
** SQLITE_OK is returned if no error is encountered (regardless of whether
|
||||
** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned
|
||||
** if some error
|
||||
** if an error occurs.
|
||||
*/
|
||||
static int walRestartLog(Wal *pWal){
|
||||
int rc = SQLITE_OK;
|
||||
@@ -2399,6 +2464,8 @@ static int walRestartLog(Wal *pWal){
|
||||
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
|
||||
assert( pInfo->aReadMark[0]==0 );
|
||||
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
|
||||
}else if( rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
walUnlockShared(pWal, WAL_READ_LOCK(0));
|
||||
@@ -2478,7 +2545,7 @@ int sqlite3WalFrames(
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
assert( pWal->szPage==szPage );
|
||||
assert( (int)pWal->szPage==szPage );
|
||||
|
||||
/* Write the log file. */
|
||||
for(p=pList; p; p=p->pDirty){
|
||||
@@ -2665,13 +2732,14 @@ int sqlite3WalCallback(Wal *pWal){
|
||||
** on the main database file before invoking this operation.
|
||||
**
|
||||
** If op is negative, then do a dry-run of the op==1 case but do
|
||||
** not actually change anything. The pager uses this to see if it
|
||||
** not actually change anything. The pager uses this to see if it
|
||||
** should acquire the database exclusive lock prior to invoking
|
||||
** the op==1 case.
|
||||
*/
|
||||
int sqlite3WalExclusiveMode(Wal *pWal, int op){
|
||||
int rc;
|
||||
assert( pWal->writeLock==0 );
|
||||
assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 );
|
||||
|
||||
/* pWal->readLock is usually set, but might be -1 if there was a
|
||||
** prior error while attempting to acquire are read-lock. This cannot
|
||||
@@ -2705,4 +2773,13 @@ int sqlite3WalExclusiveMode(Wal *pWal, int op){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the argument is non-NULL and the WAL module is using
|
||||
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
|
||||
** WAL module is using shared-memory, return false.
|
||||
*/
|
||||
int sqlite3WalHeapMemory(Wal *pWal){
|
||||
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
|
||||
}
|
||||
|
||||
#endif /* #ifndef SQLITE_OMIT_WAL */
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
# define sqlite3WalCheckpoint(u,v,w,x) 0
|
||||
# define sqlite3WalCallback(z) 0
|
||||
# define sqlite3WalExclusiveMode(y,z) 0
|
||||
# define sqlite3WalHeapMemory(z) 0
|
||||
#else
|
||||
|
||||
#define WAL_SAVEPOINT_NDATA 4
|
||||
@@ -45,7 +46,7 @@
|
||||
typedef struct Wal Wal;
|
||||
|
||||
/* Open and close a connection to a write-ahead log. */
|
||||
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**);
|
||||
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**);
|
||||
int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
|
||||
|
||||
/* Used by readers to open (lock) and close (unlock) a snapshot. A
|
||||
@@ -102,5 +103,11 @@ int sqlite3WalCallback(Wal *pWal);
|
||||
*/
|
||||
int sqlite3WalExclusiveMode(Wal *pWal, int op);
|
||||
|
||||
/* Return true if the argument is non-NULL and the WAL module is using
|
||||
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
|
||||
** WAL module is using shared-memory, return false.
|
||||
*/
|
||||
int sqlite3WalHeapMemory(Wal *pWal);
|
||||
|
||||
#endif /* ifndef SQLITE_OMIT_WAL */
|
||||
#endif /* _WAL_H_ */
|
||||
|
||||
+239
-95
@@ -192,7 +192,6 @@ struct WhereMaskSet {
|
||||
struct WhereCost {
|
||||
WherePlan plan; /* The lookup strategy */
|
||||
double rCost; /* Overall cost of pursuing this search strategy */
|
||||
double nRow; /* Estimated number of output rows */
|
||||
Bitmask used; /* Bitmask of cursors used by this plan */
|
||||
};
|
||||
|
||||
@@ -235,10 +234,11 @@ struct WhereCost {
|
||||
#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
|
||||
#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
|
||||
#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
|
||||
#define WHERE_NOT_FULLSCAN 0x000f3000 /* Does not do a full table scan */
|
||||
#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
|
||||
#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
|
||||
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
|
||||
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
|
||||
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
|
||||
#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
|
||||
#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
|
||||
#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
|
||||
@@ -669,11 +669,12 @@ static int isLikeOrGlob(
|
||||
}
|
||||
if( op==TK_VARIABLE ){
|
||||
Vdbe *pReprepare = pParse->pReprepare;
|
||||
pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE);
|
||||
int iCol = pRight->iColumn;
|
||||
pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
|
||||
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
||||
z = (char *)sqlite3_value_text(pVal);
|
||||
}
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn);
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */
|
||||
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
|
||||
}else if( op==TK_STRING ){
|
||||
z = pRight->u.zToken;
|
||||
@@ -691,7 +692,7 @@ static int isLikeOrGlob(
|
||||
*ppPrefix = pPrefix;
|
||||
if( op==TK_VARIABLE ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn);
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */
|
||||
if( *pisComplete && pRight->u.zToken[1] ){
|
||||
/* If the rhs of the LIKE expression is a variable, and the current
|
||||
** value of the variable means there is no need to invoke the LIKE
|
||||
@@ -1555,7 +1556,8 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
|
||||
** Required because bestIndex() is called by bestOrClauseIndex()
|
||||
*/
|
||||
static void bestIndex(
|
||||
Parse*, WhereClause*, struct SrcList_item*, Bitmask, ExprList*, WhereCost*);
|
||||
Parse*, WhereClause*, struct SrcList_item*,
|
||||
Bitmask, Bitmask, ExprList*, WhereCost*);
|
||||
|
||||
/*
|
||||
** This routine attempts to find an scanning strategy that can be used
|
||||
@@ -1568,7 +1570,8 @@ static void bestOrClauseIndex(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereClause *pWC, /* The WHERE clause */
|
||||
struct SrcList_item *pSrc, /* The FROM clause term to search */
|
||||
Bitmask notReady, /* Mask of cursors that are not available */
|
||||
Bitmask notReady, /* Mask of cursors not available for indexing */
|
||||
Bitmask notValid, /* Cursors not available for any purpose */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
WhereCost *pCost /* Lowest cost query plan */
|
||||
){
|
||||
@@ -1578,8 +1581,9 @@ static void bestOrClauseIndex(
|
||||
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
|
||||
WhereTerm *pTerm; /* A single term of the WHERE clause */
|
||||
|
||||
/* No OR-clause optimization allowed if the NOT INDEXED clause is used */
|
||||
if( pSrc->notIndexed ){
|
||||
/* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
|
||||
** are used */
|
||||
if( pSrc->notIndexed || pSrc->pIndex!=0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1604,7 +1608,7 @@ static void bestOrClauseIndex(
|
||||
));
|
||||
if( pOrTerm->eOperator==WO_AND ){
|
||||
WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc;
|
||||
bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost);
|
||||
bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost);
|
||||
}else if( pOrTerm->leftCursor==iCur ){
|
||||
WhereClause tempWC;
|
||||
tempWC.pParse = pWC->pParse;
|
||||
@@ -1612,12 +1616,12 @@ static void bestOrClauseIndex(
|
||||
tempWC.op = TK_AND;
|
||||
tempWC.a = pOrTerm;
|
||||
tempWC.nTerm = 1;
|
||||
bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost);
|
||||
bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
rTotal += sTermCost.rCost;
|
||||
nRow += sTermCost.nRow;
|
||||
nRow += sTermCost.plan.nRow;
|
||||
used |= sTermCost.used;
|
||||
if( rTotal>=pCost->rCost ) break;
|
||||
}
|
||||
@@ -1636,8 +1640,8 @@ static void bestOrClauseIndex(
|
||||
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
|
||||
if( rTotal<pCost->rCost ){
|
||||
pCost->rCost = rTotal;
|
||||
pCost->nRow = nRow;
|
||||
pCost->used = used;
|
||||
pCost->plan.nRow = nRow;
|
||||
pCost->plan.wsFlags = flags;
|
||||
pCost->plan.u.pTerm = pTerm;
|
||||
}
|
||||
@@ -1705,7 +1709,7 @@ static void bestAutomaticIndex(
|
||||
|
||||
assert( pParse->nQueryLoop >= (double)1 );
|
||||
pTable = pSrc->pTab;
|
||||
nTableRow = pTable->pIndex ? pTable->pIndex->aiRowEst[0] : 1000000;
|
||||
nTableRow = pTable->nRowEst;
|
||||
logN = estLog(nTableRow);
|
||||
costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
|
||||
if( costTempIdx>=pCost->rCost ){
|
||||
@@ -1721,7 +1725,7 @@ static void bestAutomaticIndex(
|
||||
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
|
||||
pCost->rCost, costTempIdx));
|
||||
pCost->rCost = costTempIdx;
|
||||
pCost->nRow = logN + 1;
|
||||
pCost->plan.nRow = logN + 1;
|
||||
pCost->plan.wsFlags = WHERE_TEMP_INDEX;
|
||||
pCost->used = pTerm->prereqRight;
|
||||
break;
|
||||
@@ -2059,7 +2063,8 @@ static void bestVirtualIndex(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereClause *pWC, /* The WHERE clause */
|
||||
struct SrcList_item *pSrc, /* The FROM clause term to search */
|
||||
Bitmask notReady, /* Mask of cursors that are not available */
|
||||
Bitmask notReady, /* Mask of cursors not available for index */
|
||||
Bitmask notValid, /* Cursors not valid for any purpose */
|
||||
ExprList *pOrderBy, /* The order by clause */
|
||||
WhereCost *pCost, /* Lowest cost query plan */
|
||||
sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */
|
||||
@@ -2189,7 +2194,7 @@ static void bestVirtualIndex(
|
||||
/* Try to find a more efficient access pattern by using multiple indexes
|
||||
** to optimize an OR expression within the WHERE clause.
|
||||
*/
|
||||
bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
|
||||
bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@@ -2310,12 +2315,11 @@ static int valueFromExpr(
|
||||
u8 aff,
|
||||
sqlite3_value **pp
|
||||
){
|
||||
/* The evalConstExpr() function will have already converted any TK_VARIABLE
|
||||
** expression involved in an comparison into a TK_REGISTER. */
|
||||
assert( pExpr->op!=TK_VARIABLE );
|
||||
if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){
|
||||
if( pExpr->op==TK_VARIABLE
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
|
||||
){
|
||||
int iVar = pExpr->iColumn;
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */
|
||||
*pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -2470,7 +2474,8 @@ static void bestBtreeIndex(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereClause *pWC, /* The WHERE clause */
|
||||
struct SrcList_item *pSrc, /* The FROM clause term to search */
|
||||
Bitmask notReady, /* Mask of cursors that are not available */
|
||||
Bitmask notReady, /* Mask of cursors not available for indexing */
|
||||
Bitmask notValid, /* Cursors not available for any purpose */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
WhereCost *pCost /* Lowest cost query plan */
|
||||
){
|
||||
@@ -2512,23 +2517,14 @@ static void bestBtreeIndex(
|
||||
sPk.nColumn = 1;
|
||||
sPk.aiColumn = &aiColumnPk;
|
||||
sPk.aiRowEst = aiRowEstPk;
|
||||
aiRowEstPk[1] = 1;
|
||||
sPk.onError = OE_Replace;
|
||||
sPk.pTable = pSrc->pTab;
|
||||
aiRowEstPk[0] = pSrc->pTab->nRowEst;
|
||||
aiRowEstPk[1] = 1;
|
||||
pFirst = pSrc->pTab->pIndex;
|
||||
if( pSrc->notIndexed==0 ){
|
||||
sPk.pNext = pFirst;
|
||||
}
|
||||
/* The aiRowEstPk[0] is an estimate of the total number of rows in the
|
||||
** table. Get this information from the ANALYZE information if it is
|
||||
** available. If not available, assume the table 1 million rows in size.
|
||||
*/
|
||||
if( pFirst ){
|
||||
assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */
|
||||
aiRowEstPk[0] = pFirst->aiRowEst[0];
|
||||
}else{
|
||||
aiRowEstPk[0] = 1000000;
|
||||
}
|
||||
pProbe = &sPk;
|
||||
wsFlagMask = ~(
|
||||
WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
|
||||
@@ -2741,16 +2737,16 @@ static void bestBtreeIndex(
|
||||
** with this step if we already know this index will not be chosen.
|
||||
** Also, never reduce the output row count below 2 using this step.
|
||||
**
|
||||
** Do not reduce the output row count if pSrc is the only table that
|
||||
** is notReady; if notReady is a power of two. This will be the case
|
||||
** when the main sqlite3WhereBegin() loop is scanning for a table with
|
||||
** and "optimal" index, and on such a scan the output row count
|
||||
** reduction is not valid because it does not update the "pCost->used"
|
||||
** bitmap. The notReady bitmap will also be a power of two when we
|
||||
** are scanning for the last table in a 64-way join. We are willing
|
||||
** to bypass this optimization in that corner case.
|
||||
** It is critical that the notValid mask be used here instead of
|
||||
** the notReady mask. When computing an "optimal" index, the notReady
|
||||
** mask will only have one bit set - the bit for the current table.
|
||||
** The notValid mask, on the other hand, always has all bits set for
|
||||
** tables that are not in outer loops. If notReady is used here instead
|
||||
** of notValid, then a optimal index that depends on inner joins loops
|
||||
** might be selected even when there exists an optimal index that has
|
||||
** no such dependency.
|
||||
*/
|
||||
if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){
|
||||
if( nRow>2 && cost<=pCost->rCost ){
|
||||
int k; /* Loop counter */
|
||||
int nSkipEq = nEq; /* Number of == constraints to skip */
|
||||
int nSkipRange = nBound; /* Number of < constraints to skip */
|
||||
@@ -2759,7 +2755,7 @@ static void bestBtreeIndex(
|
||||
thisTab = getMask(pWC->pMaskSet, iCur);
|
||||
for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
|
||||
if( (pTerm->prereqAll & notReady)!=thisTab ) continue;
|
||||
if( (pTerm->prereqAll & notValid)!=thisTab ) continue;
|
||||
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
|
||||
if( nSkipEq ){
|
||||
/* Ignore the first nEq equality matches since the index
|
||||
@@ -2801,11 +2797,11 @@ static void bestBtreeIndex(
|
||||
** index and its cost in the pCost structure.
|
||||
*/
|
||||
if( (!pIdx || wsFlags)
|
||||
&& (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->nRow))
|
||||
&& (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->plan.nRow))
|
||||
){
|
||||
pCost->rCost = cost;
|
||||
pCost->nRow = nRow;
|
||||
pCost->used = used;
|
||||
pCost->plan.nRow = nRow;
|
||||
pCost->plan.wsFlags = (wsFlags&wsFlagMask);
|
||||
pCost->plan.nEq = nEq;
|
||||
pCost->plan.u.pIdx = pIdx;
|
||||
@@ -2841,7 +2837,7 @@ static void bestBtreeIndex(
|
||||
pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk")
|
||||
));
|
||||
|
||||
bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
|
||||
bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
|
||||
bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost);
|
||||
pCost->plan.wsFlags |= eqTermMask;
|
||||
}
|
||||
@@ -2856,14 +2852,15 @@ static void bestIndex(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereClause *pWC, /* The WHERE clause */
|
||||
struct SrcList_item *pSrc, /* The FROM clause term to search */
|
||||
Bitmask notReady, /* Mask of cursors that are not available */
|
||||
Bitmask notReady, /* Mask of cursors not available for indexing */
|
||||
Bitmask notValid, /* Cursors not available for any purpose */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
WhereCost *pCost /* Lowest cost query plan */
|
||||
){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pSrc->pTab) ){
|
||||
sqlite3_index_info *p = 0;
|
||||
bestVirtualIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost, &p);
|
||||
bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p);
|
||||
if( p->needToFreeIdxStr ){
|
||||
sqlite3_free(p->idxStr);
|
||||
}
|
||||
@@ -2871,7 +2868,7 @@ static void bestIndex(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
bestBtreeIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
|
||||
bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3133,6 +3130,161 @@ static int codeAllEqualityTerms(
|
||||
return regBase;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/*
|
||||
** This routine is a helper for explainIndexRange() below
|
||||
**
|
||||
** pStr holds the text of an expression that we are building up one term
|
||||
** at a time. This routine adds a new term to the end of the expression.
|
||||
** Terms are separated by AND so add the "AND" text for second and subsequent
|
||||
** terms only.
|
||||
*/
|
||||
static void explainAppendTerm(
|
||||
StrAccum *pStr, /* The text expression being built */
|
||||
int iTerm, /* Index of this term. First is zero */
|
||||
const char *zColumn, /* Name of the column */
|
||||
const char *zOp /* Name of the operator */
|
||||
){
|
||||
if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
sqlite3StrAccumAppend(pStr, zColumn, -1);
|
||||
sqlite3StrAccumAppend(pStr, zOp, 1);
|
||||
sqlite3StrAccumAppend(pStr, "?", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument pLevel describes a strategy for scanning table pTab. This
|
||||
** function returns a pointer to a string buffer containing a description
|
||||
** of the subset of table rows scanned by the strategy in the form of an
|
||||
** SQL expression. Or, if all rows are scanned, NULL is returned.
|
||||
**
|
||||
** For example, if the query:
|
||||
**
|
||||
** SELECT * FROM t1 WHERE a=1 AND b>2;
|
||||
**
|
||||
** is run and there is an index on (a, b), then this function returns a
|
||||
** string similar to:
|
||||
**
|
||||
** "a=? AND b>?"
|
||||
**
|
||||
** The returned pointer points to memory obtained from sqlite3DbMalloc().
|
||||
** It is the responsibility of the caller to free the buffer when it is
|
||||
** no longer required.
|
||||
*/
|
||||
static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
|
||||
WherePlan *pPlan = &pLevel->plan;
|
||||
Index *pIndex = pPlan->u.pIdx;
|
||||
int nEq = pPlan->nEq;
|
||||
int i, j;
|
||||
Column *aCol = pTab->aCol;
|
||||
int *aiColumn = pIndex->aiColumn;
|
||||
StrAccum txt;
|
||||
|
||||
if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
|
||||
return 0;
|
||||
}
|
||||
sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
|
||||
txt.db = db;
|
||||
sqlite3StrAccumAppend(&txt, " (", 2);
|
||||
for(i=0; i<nEq; i++){
|
||||
explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "=");
|
||||
}
|
||||
|
||||
j = i;
|
||||
if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
|
||||
explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">");
|
||||
}
|
||||
if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
|
||||
explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(&txt, ")", 1);
|
||||
return sqlite3StrAccumFinish(&txt);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
|
||||
** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
|
||||
** record is added to the output to describe the table scan strategy in
|
||||
** pLevel.
|
||||
*/
|
||||
static void explainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
int iLevel, /* Value for "level" column of output */
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
if( pParse->explain==2 ){
|
||||
u32 flags = pLevel->plan.wsFlags;
|
||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zMsg; /* Text to add to EQP output */
|
||||
sqlite3_int64 nRow; /* Expected number of rows visited by scan */
|
||||
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
|
||||
|
||||
isSearch = (pLevel->plan.nEq>0)
|
||||
|| (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|
||||
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
||||
|
||||
zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
|
||||
if( pItem->pSelect ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId);
|
||||
}else{
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName);
|
||||
}
|
||||
|
||||
if( pItem->zAlias ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
|
||||
}
|
||||
if( (flags & WHERE_INDEXED)!=0 ){
|
||||
char *zWhere = explainIndexRange(db, pLevel, pItem->pTab);
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
|
||||
((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
|
||||
((flags & WHERE_IDX_ONLY)?"COVERING ":""),
|
||||
((flags & WHERE_TEMP_INDEX)?"":" "),
|
||||
((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName),
|
||||
zWhere
|
||||
);
|
||||
sqlite3DbFree(db, zWhere);
|
||||
}else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
|
||||
|
||||
if( flags&WHERE_ROWID_EQ ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
|
||||
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
|
||||
}else if( flags&WHERE_BTM_LIMIT ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
|
||||
}else if( flags&WHERE_TOP_LIMIT ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
|
||||
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
|
||||
pVtabIdx->idxNum, pVtabIdx->idxStr);
|
||||
}
|
||||
#endif
|
||||
if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){
|
||||
testcase( wctrlFlags & WHERE_ORDERBY_MIN );
|
||||
nRow = 1;
|
||||
}else{
|
||||
nRow = (sqlite3_int64)pLevel->plan.nRow;
|
||||
}
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define explainOneScan(u,v,w,x,y,z)
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
||||
** implementation described by pWInfo.
|
||||
@@ -3540,7 +3692,7 @@ static Bitmask codeOneLoopStart(
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT );
|
||||
testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT );
|
||||
if( pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){
|
||||
if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont);
|
||||
}
|
||||
@@ -3674,6 +3826,9 @@ static Bitmask codeOneLoopStart(
|
||||
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
|
||||
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
|
||||
if( pSubWInfo ){
|
||||
explainOneScan(
|
||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||
);
|
||||
if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||
int r;
|
||||
@@ -4069,6 +4224,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
|
||||
memset(&bestPlan, 0, sizeof(bestPlan));
|
||||
bestPlan.rCost = SQLITE_BIG_DBL;
|
||||
WHERETRACE(("*** Begin search for loop %d ***\n", i));
|
||||
|
||||
/* Loop through the remaining entries in the FROM clause to find the
|
||||
** next nested loop. The loop tests all FROM clause entries
|
||||
@@ -4087,9 +4243,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** other FROM clause terms that are notReady. If no notReady terms are
|
||||
** used then the "optimal" query plan works.
|
||||
**
|
||||
** Note that the WhereCost.nRow parameter for an optimal scan might
|
||||
** not be as small as it would be if the table really were the innermost
|
||||
** join. The nRow value can be reduced by WHERE clause constraints
|
||||
** that do not use indices. But this nRow reduction only happens if the
|
||||
** table really is the innermost join.
|
||||
**
|
||||
** The second loop iteration is only performed if no optimal scan
|
||||
** strategies were found by the first loop. This 2nd iteration is used to
|
||||
** search for the lowest cost scan overall.
|
||||
** strategies were found by the first iteration. This second iteration
|
||||
** is used to search for the lowest cost scan overall.
|
||||
**
|
||||
** Previous versions of SQLite performed only the second iteration -
|
||||
** the next outermost loop was always that with the lowest overall
|
||||
@@ -4102,14 +4264,14 @@ WhereInfo *sqlite3WhereBegin(
|
||||
**
|
||||
** The best strategy is to iterate through table t1 first. However it
|
||||
** is not possible to determine this with a simple greedy algorithm.
|
||||
** However, since the cost of a linear scan through table t2 is the same
|
||||
** Since the cost of a linear scan through table t2 is the same
|
||||
** as the cost of a linear scan through table t1, a simple greedy
|
||||
** algorithm may choose to use t2 for the outer loop, which is a much
|
||||
** costlier approach.
|
||||
*/
|
||||
nUnconstrained = 0;
|
||||
notIndexed = 0;
|
||||
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){
|
||||
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
|
||||
Bitmask mask; /* Mask of tables not yet ready */
|
||||
for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
|
||||
int doNotReorder; /* True if this table should not be reordered */
|
||||
@@ -4127,15 +4289,19 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
|
||||
if( pTabItem->pIndex==0 ) nUnconstrained++;
|
||||
|
||||
WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
|
||||
j, isOptimal));
|
||||
assert( pTabItem->pTab );
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTabItem->pTab) ){
|
||||
sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo;
|
||||
bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp);
|
||||
bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
|
||||
&sCost, pp);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost);
|
||||
bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
|
||||
&sCost);
|
||||
}
|
||||
assert( isOptimal || (sCost.used¬Ready)==0 );
|
||||
|
||||
@@ -4175,10 +4341,12 @@ WhereInfo *sqlite3WhereBegin(
|
||||
&& (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */
|
||||
|| NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
|
||||
&& (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */
|
||||
|| (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow))
|
||||
|| (sCost.rCost<=bestPlan.rCost
|
||||
&& sCost.plan.nRow<bestPlan.plan.nRow))
|
||||
){
|
||||
WHERETRACE(("... best so far with cost=%g and nRow=%g\n",
|
||||
sCost.rCost, sCost.nRow));
|
||||
WHERETRACE(("=== table %d is best so far"
|
||||
" with cost=%g and nRow=%g\n",
|
||||
j, sCost.rCost, sCost.plan.nRow));
|
||||
bestPlan = sCost;
|
||||
bestJ = j;
|
||||
}
|
||||
@@ -4187,8 +4355,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
assert( bestJ>=0 );
|
||||
assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
|
||||
WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ,
|
||||
pLevel-pWInfo->a));
|
||||
WHERETRACE(("*** Optimizer selects table %d for loop %d"
|
||||
" with cost=%g and nRow=%g\n",
|
||||
bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow));
|
||||
if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
|
||||
*ppOrderBy = 0;
|
||||
}
|
||||
@@ -4203,7 +4372,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor);
|
||||
pLevel->iFrom = (u8)bestJ;
|
||||
if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.nRow;
|
||||
if( bestPlan.plan.nRow>=(double)1 ){
|
||||
pParse->nQueryLoop *= bestPlan.plan.nRow;
|
||||
}
|
||||
|
||||
/* Check that if the table scanned by this loop iteration had an
|
||||
** INDEXED BY clause attached to it, that the named index is being
|
||||
@@ -4251,44 +4422,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
*/
|
||||
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
|
||||
notReady = ~(Bitmask)0;
|
||||
pWInfo->nRowOut = (double)1;
|
||||
for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
|
||||
Table *pTab; /* Table to open */
|
||||
int iDb; /* Index of database containing table/index */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg;
|
||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||
zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName);
|
||||
if( pItem->zAlias ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
|
||||
}
|
||||
if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s WITH AUTOMATIC INDEX", zMsg);
|
||||
}else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s",
|
||||
zMsg, pLevel->plan.u.pIdx->zName);
|
||||
}else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s VIA MULTI-INDEX UNION", zMsg);
|
||||
}else if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
else if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
|
||||
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
|
||||
pVtabIdx->idxNum, pVtabIdx->idxStr);
|
||||
}
|
||||
#endif
|
||||
if( pLevel->plan.wsFlags & WHERE_ORDERBY ){
|
||||
zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
pTabItem = &pTabList->a[pLevel->iFrom];
|
||||
pTab = pTabItem->pTab;
|
||||
pLevel->iTabCur = pTabItem->iCursor;
|
||||
pWInfo->nRowOut *= pLevel->plan.nRow;
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
|
||||
/* Do nothing */
|
||||
@@ -4344,8 +4486,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
*/
|
||||
notReady = ~(Bitmask)0;
|
||||
for(i=0; i<nTabList; i++){
|
||||
pLevel = &pWInfo->a[i];
|
||||
explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
|
||||
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
|
||||
pWInfo->iContinue = pWInfo->a[i].addrCont;
|
||||
pWInfo->iContinue = pLevel->addrCont;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST /* For testing and debugging use only */
|
||||
|
||||
Reference in New Issue
Block a user