Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -305,14 +305,10 @@ #include #include #include #include "fts3.h" -#ifndef SQLITE_CORE -# include "sqlite4ext.h" - SQLITE_EXTENSION_INIT1 -#endif static int fts3EvalNext(Fts3Cursor *pCsr); static int fts3EvalStart(Fts3Cursor *pCsr); static int fts3TermSegReaderCursor( Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); Index: ext/fts3/fts3Int.h ================================================================== --- ext/fts3/fts3Int.h +++ ext/fts3/fts3Int.h @@ -27,16 +27,10 @@ # define SQLITE_ENABLE_FTS3 #endif #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) -/* If not building as part of the core, include sqlite4ext.h. */ -#ifndef SQLITE_CORE -# include "sqlite4ext.h" -extern const sqlite4_api_routines *sqlite4_api; -#endif - #include "sqlite4.h" #include "fts3_tokenizer.h" #include "fts3_hash.h" /* Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -49,25 +49,24 @@ TCCX += -I$(TOP)/ext/async # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o \ - backup.o bitvec.o btmutex.o btree.o build.o \ + bitvec.o build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \ fts3_write.o func.o global.o hash.o \ - icu.o insert.o journal.o kvmem.o legacy.o loadext.o \ + icu.o insert.o kvmem.o legacy.o \ main.o malloc.o math.o mem0.o mem1.o mem2.o mem3.o mem5.o \ - memjournal.o \ mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ - notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ - pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ + opcodes.o os.o os_os2.o os_unix.o os_win.o \ + parse.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o storage.o \ table.o tokenize.o trigger.o \ update.o util.o varint.o \ - vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbecodec.o \ + vdbe.o vdbeapi.o vdbeaux.o vdbecodec.o vdbecursor.o \ vdbemem.o vdbesort.o vdbetrace.o \ walker.o where.o utf.o vtab.o @@ -76,16 +75,11 @@ SRC = \ $(TOP)/src/alter.c \ $(TOP)/src/analyze.c \ $(TOP)/src/attach.c \ $(TOP)/src/auth.c \ - $(TOP)/src/backup.c \ $(TOP)/src/bitvec.c \ - $(TOP)/src/btmutex.c \ - $(TOP)/src/btree.c \ - $(TOP)/src/btree.h \ - $(TOP)/src/btreeInt.h \ $(TOP)/src/build.c \ $(TOP)/src/callback.c \ $(TOP)/src/complete.c \ $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ @@ -97,52 +91,42 @@ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ - $(TOP)/src/journal.c \ $(TOP)/src/kvmem.c \ $(TOP)/src/legacy.c \ - $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/math.c \ $(TOP)/src/mem0.c \ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ - $(TOP)/src/memjournal.c \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ $(TOP)/src/mutex_os2.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ - $(TOP)/src/notify.c \ $(TOP)/src/os.c \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ - $(TOP)/src/pager.c \ - $(TOP)/src/pager.h \ $(TOP)/src/parse.y \ - $(TOP)/src/pcache.c \ - $(TOP)/src/pcache.h \ - $(TOP)/src/pcache1.c \ $(TOP)/src/pragma.c \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/resolve.c \ $(TOP)/src/rowset.c \ $(TOP)/src/select.c \ $(TOP)/src/shell.c \ $(TOP)/src/sqlite.h.in \ - $(TOP)/src/sqlite4ext.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/status.c \ $(TOP)/src/storage.c \ $(TOP)/src/storage.h \ @@ -156,11 +140,10 @@ $(TOP)/src/varint.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbe.h \ $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ - $(TOP)/src/vdbeblob.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbesort.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ @@ -168,28 +151,10 @@ $(TOP)/src/where.c # Source code for extensions # SRC += \ - $(TOP)/ext/fts1/fts1.c \ - $(TOP)/ext/fts1/fts1.h \ - $(TOP)/ext/fts1/fts1_hash.c \ - $(TOP)/ext/fts1/fts1_hash.h \ - $(TOP)/ext/fts1/fts1_porter.c \ - $(TOP)/ext/fts1/fts1_tokenizer.h \ - $(TOP)/ext/fts1/fts1_tokenizer1.c -SRC += \ - $(TOP)/ext/fts2/fts2.c \ - $(TOP)/ext/fts2/fts2.h \ - $(TOP)/ext/fts2/fts2_hash.c \ - $(TOP)/ext/fts2/fts2_hash.h \ - $(TOP)/ext/fts2/fts2_icu.c \ - $(TOP)/ext/fts2/fts2_porter.c \ - $(TOP)/ext/fts2/fts2_tokenizer.h \ - $(TOP)/ext/fts2/fts2_tokenizer.c \ - $(TOP)/ext/fts2/fts2_tokenizer1.c -SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ @@ -225,42 +190,31 @@ # TESTSRC = \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/src/test1.c \ - $(TOP)/src/test2.c \ - $(TOP)/src/test3.c \ $(TOP)/src/test4.c \ $(TOP)/src/test5.c \ $(TOP)/src/test6.c \ $(TOP)/src/test7.c \ $(TOP)/src/test8.c \ $(TOP)/src/test9.c \ - $(TOP)/src/test_autoext.c \ - $(TOP)/src/test_backup.c \ - $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ - $(TOP)/src/test_func.c \ $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ - $(TOP)/src/test_pcache.c \ - $(TOP)/src/test_quota.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ - $(TOP)/src/test_server.c \ $(TOP)/src/test_storage.c \ - $(TOP)/src/test_superlock.c \ - $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c @@ -268,12 +222,10 @@ #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c \ - $(TOP)/src/backup.c \ - $(TOP)/src/btree.c \ $(TOP)/src/build.c \ $(TOP)/src/date.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ @@ -280,17 +232,14 @@ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ - $(TOP)/src/pager.c \ $(TOP)/src/pragma.c \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ - $(TOP)/src/pcache.c \ - $(TOP)/src/pcache1.c \ $(TOP)/src/select.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbeapi.c \ @@ -306,41 +255,26 @@ $(TOP)/ext/fts3/fts3_write.c # Header files used by all library source files. # HDR = \ - $(TOP)/src/btree.h \ - $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ - $(TOP)/src/pager.h \ - $(TOP)/src/pcache.h \ parse.h \ sqlite4.h \ - $(TOP)/src/sqlite4ext.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/storage.h \ $(TOP)/src/vdbe.h \ $(TOP)/src/vdbeInt.h -# Header files used by extensions -# -EXTHDR += \ - $(TOP)/ext/fts1/fts1.h \ - $(TOP)/ext/fts1/fts1_hash.h \ - $(TOP)/ext/fts1/fts1_tokenizer.h -EXTHDR += \ - $(TOP)/ext/fts2/fts2.h \ - $(TOP)/ext/fts2/fts2_hash.h \ - $(TOP)/ext/fts2/fts2_tokenizer.h -EXTHDR += \ +EXTHDR = \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_tokenizer.h EXTHDR += \ Index: notes/key_encoding.txt ================================================================== --- notes/key_encoding.txt +++ notes/key_encoding.txt @@ -90,44 +90,62 @@ Finite non-zero values are classified as either large, or small. Small values have an absolute value less than 1. Large values have an absolute value of 1 or more. The value 1.0 is considered large. For both large and small values, we compute a mantissa M and an -exponent E. The mantissa is a base-10 representation of the +exponent E. The mantissa is a base-100 representation of the value. The exponent E determines where to put the decimal point. -Each decimal digit of the mantissa is stored in a half-byte. There -are two decimal digits per byte. Digit 0 has a value of 1. Digit 9 -has a value of A (binary 1010). The other digits have values in -between. A value of 0 is the end-of-content marker. The mantissa -is composed of one or more bytes, ending with a single byte where -the lower four bits at least are the end-of-content mark. The upper -four bits of the last byte may or may not be zero too, depending on -how many other digits exist in the mantissa. +Each centimal digit of the mantissa is stored in a byte. If the +value of the centimal digit is X (hence X>=0 and X<=99) then the +byte value will be 2*X+1 for every byte of the mantissa, except +for the last byte which will be 2*X+0. If we assume all digits of the mantissa occur to the right of the -decimal point, then the exponent E is one less than the power of ten -by which one must multiply the mantissa to recover the original value. -Or, E is one less than the number of digits that occur to the left of -the decimal point. +decimal point, then the exponent E is the power of one hundred +by which one must multiply the mantissa to recover the original +value. Examples: - Value Exponent E Significand M (in hex) - -------- ---------- ---------------------- - 1.0 0 20 - 100.0 2 20 - 1234 3 23 45 00 - 12345 4 23 45 60 - 12.345 1 23 45 60 - 0.123 -1 23 40 + Value Exponent E Significand M (in hex) + -------- ---------- ---------------------- + 1.0 1 02 + 10.0 1 14 + 99.0 1 b4 + 99.01 1 b5 02 + 99.0001 1 b5 01 02 + 100.0 2 03 00 + 100.1 2 03 02 + 100.01 2 03 01 02 + 1234 2 19 44 + 9999 2 c7 c6 + 9999.000001 2 c7 c7 01 01 02 + 9999.000009 2 c7 c7 01 01 12 + 9999.00001 2 c7 c7 01 01 14 + 9999.00009 2 c7 c7 01 01 b4 + 9999.000099 2 c7 c7 01 01 c6 + 9999.0001 2 c7 c7 01 02 + 9999.001 2 c7 c7 01 14 + 9999.01 2 c7 c7 02 + 9999.1 2 c7 c7 14 + 10000 3 02 + 10001 3 03 00 02 + 12345 3 03 2f 5a + 123450 4 19 45 64 + 1234.5 3 19 45 64 + 12.345 2 19 45 64 + 0.123 0 19 3c + 0.0123 0 03 2e + 0.00123 -1 19 3c + 9223372036854775807 10 13 2d 43 91 07 89 6d 9b 75 0e The E value is stored in the encoding as an unsigned varint. And since E can be negative, that means we need separate cases for positive and negative E value. That is why large and small numbers are treated -differently. Large numbers have a positive or zero E and small numbers -have a negative E. +differently. Large numbers have a positive E and small numbers +have a zero or negative E. Large negative numbers have an initial byte of 0x08 followed by the ones-complement of the varint of E followed by the ones-complement of M. Small negative numbers have an initial byte of 0x09 followed by the varint of -E followed by the ones-complement of M. Small positive @@ -136,11 +154,12 @@ an initial byte of 0x0c followed by the varint of E followed by M. SUMMARY Each SQL value is encoded as one or more bytes. The first byte of -the encoding is as follows: +the encoding, its meaning, and a terse description of the bytes that +follow is given by the following table: 0x05 NULL 0x06 NaN 0x07 negative infinity 0x08 negative-large ~E ~M Index: src/alter.c ================================================================== --- src/alter.c +++ src/alter.c @@ -337,11 +337,10 @@ Trigger *pTrig; #endif v = sqlite4GetVdbe(pParse); if( NEVER(v==0) ) return; - assert( sqlite4BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); assert( iDb>=0 ); #ifndef SQLITE_OMIT_TRIGGER /* Drop any table triggers from the internal schema. */ @@ -410,11 +409,10 @@ int savedDbFlags; /* Saved value of db->flags */ savedDbFlags = db->flags; if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); - assert( sqlite4BtreeHoldsAllMutexes(pParse->db) ); pTab = sqlite4LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase); if( !pTab ) goto exit_rename_table; iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zName; @@ -585,35 +583,10 @@ sqlite4DbFree(db, zName); db->flags = savedDbFlags; } -/* -** Generate code to make sure the file format number is at least minFormat. -** The generated code will increase the file format number if necessary. -*/ -void sqlite4MinimumFileFormat(Parse *pParse, int iDb, int minFormat){ - Vdbe *v; - v = sqlite4GetVdbe(pParse); - /* The VDBE should have been allocated before this routine is called. - ** If that allocation failed, we would have quit before reaching this - ** point */ - if( ALWAYS(v) ){ - int r1 = sqlite4GetTempReg(pParse); - int r2 = sqlite4GetTempReg(pParse); - int j1; - sqlite4VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); - sqlite4VdbeUsesBtree(v, iDb); - sqlite4VdbeAddOp2(v, OP_Integer, minFormat, r2); - j1 = sqlite4VdbeAddOp3(v, OP_Ge, r2, 0, r1); - sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2); - sqlite4VdbeJumpHere(v, j1); - sqlite4ReleaseTempReg(pParse, r1); - sqlite4ReleaseTempReg(pParse, r2); - } -} - /* ** This function is called after an "ALTER TABLE ... ADD" statement ** has been parsed. Argument pColDef contains the text of the new ** column definition. ** @@ -634,11 +607,10 @@ db = pParse->db; if( pParse->nErr || db->mallocFailed ) return; pNew = pParse->pNewTable; assert( pNew ); - assert( sqlite4BtreeHoldsAllMutexes(db) ); iDb = sqlite4SchemaToIndex(db, pNew->pSchema); zDb = db->aDb[iDb].zName; zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ pCol = &pNew->aCol[pNew->nCol-1]; pDflt = pCol->pDflt; @@ -717,16 +689,10 @@ ); sqlite4DbFree(db, zCol); db->flags = savedDbFlags; } - /* If the default value of the new column is NULL, then set the file - ** format to 2. If the default value of the new column is not NULL, - ** the file format becomes 3. - */ - sqlite4MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); - /* Reload the schema of the modified table. */ reloadTableSchema(pParse, pTab, pTab->zName); } /* @@ -753,11 +719,10 @@ int nAlloc; sqlite4 *db = pParse->db; /* Look up the table being altered. */ assert( pParse->pNewTable==0 ); - assert( sqlite4BtreeHoldsAllMutexes(db) ); if( db->mallocFailed ) goto exit_begin_add_column; pTab = sqlite4LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase); if( !pTab ) goto exit_begin_add_column; #ifndef SQLITE_OMIT_VIRTUALTABLE Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -155,11 +155,10 @@ int i; sqlite4 *db = pParse->db; Db *pDb; Vdbe *v = sqlite4GetVdbe(pParse); if( v==0 ) return; - assert( sqlite4BtreeHoldsAllMutexes(db) ); assert( sqlite4VdbeDb(v)==db ); pDb = &db->aDb[iDb]; /* Create new statistic tables if they do not exist, or clear them ** if they do already exist. @@ -476,14 +475,12 @@ } if( memcmp(pTab->zName, "sqlite_", 7)==0 ){ /* Do not gather statistics on system tables */ return; } - assert( sqlite4BtreeHoldsAllMutexes(db) ); iDb = sqlite4SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite4AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ return; } @@ -725,11 +722,10 @@ sqlite4BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 3; openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); } loadAnalysis(pParse, iDb); @@ -743,11 +739,10 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ int iDb; int iStatCur; assert( pTab!=0 ); - assert( sqlite4BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); sqlite4BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 3; if( pOnlyIdx ){ @@ -780,11 +775,10 @@ Index *pIdx; Token *pTableName; /* Read the database schema. If an error occurs, leave an error message ** and code in pParse and return NULL. */ - assert( sqlite4BtreeHoldsAllMutexes(pParse->db) ); if( SQLITE_OK!=sqlite4ReadSchema(pParse) ){ return; } assert( pName2!=0 || pName1==0 ); @@ -1071,14 +1065,13 @@ HashElem *i; char *zSql; int rc; assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); + assert( db->aDb[iDb].pKV!=0 ); /* Clear any prior statistics */ - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite4DefaultRowEst(pIdx); #ifdef SQLITE_ENABLE_STAT3 sqlite4DeleteIndexSamples(db, pIdx); Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -136,87 +136,43 @@ sqlite4_free(zErr); return; } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite4BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); - if( rc==SQLITE_OK ) sqlite4KVStoreOpen(zPath, &aNew->pKV); + rc = sqlite4KVStoreOpen(db, zName, zPath, &aNew->pKV, 0); sqlite4_free( zPath ); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite4MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ - Pager *pPager; - aNew->pSchema = sqlite4SchemaGet(db, aNew->pBt); + aNew->pSchema = sqlite4SchemaGet(db); if( !aNew->pSchema ){ rc = SQLITE_NOMEM; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite4MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } - pPager = sqlite4BtreePager(aNew->pBt); - sqlite4PagerLockingMode(pPager, db->dfltLockMode); - /*sqlite4BtreeSecureDelete(aNew->pBt, - sqlite4BtreeSecureDelete(db->aDb[0].pBt,-1) );*/ } - aNew->safety_level = 3; aNew->zName = sqlite4DbStrDup(db, zName); if( rc==SQLITE_OK && aNew->zName==0 ){ rc = SQLITE_NOMEM; } - -#ifdef SQLITE_HAS_CODEC - if( rc==SQLITE_OK ){ - extern int sqlite4CodecAttach(sqlite4*, int, const void*, int); - extern void sqlite4CodecGetKey(sqlite4*, int, void**, int*); - int nKey; - char *zKey; - int t = sqlite4_value_type(argv[2]); - switch( t ){ - case SQLITE_INTEGER: - case SQLITE_FLOAT: - zErrDyn = sqlite4DbStrDup(db, "Invalid key value"); - rc = SQLITE_ERROR; - break; - - case SQLITE_TEXT: - case SQLITE_BLOB: - nKey = sqlite4_value_bytes(argv[2]); - zKey = (char *)sqlite4_value_blob(argv[2]); - rc = sqlite4CodecAttach(db, db->nDb-1, zKey, nKey); - break; - - case SQLITE_NULL: - /* No key specified. Use the key from the main database */ - sqlite4CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey>0 || sqlite4BtreeGetReserve(db->aDb[0].pBt)>0 ){ - rc = sqlite4CodecAttach(db, db->nDb-1, zKey, nKey); - } - break; - } - } -#endif - /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and ** remove the entry from the db->aDb[] array. i.e. put everything back the way ** we found it. */ if( rc==SQLITE_OK ){ - sqlite4BtreeEnterAll(db); rc = sqlite4Init(db, &zErrDyn); - sqlite4BtreeLeaveAll(db); } if( rc ){ int iDb = db->nDb - 1; assert( iDb>=2 ); - if( db->aDb[iDb].pBt ){ - sqlite4BtreeClose(db->aDb[iDb].pBt); - db->aDb[iDb].pBt = 0; + if( db->aDb[iDb].pKV ){ sqlite4KVStoreClose(db->aDb[iDb].pKV); db->aDb[iDb].pKV = 0; db->aDb[iDb].pSchema = 0; } sqlite4ResetInternalSchema(db, -1); @@ -264,11 +220,11 @@ UNUSED_PARAMETER(NotUsed); if( zName==0 ) zName = ""; for(i=0; inDb; i++){ pDb = &db->aDb[i]; - if( pDb->pBt==0 ) continue; + if( pDb->pKV==0 ) continue; if( sqlite4StrICmp(pDb->zName, zName)==0 ) break; } if( i>=db->nDb ){ sqlite4_snprintf(sizeof(zErr),zErr, "no such database: %s", zName); @@ -281,17 +237,15 @@ if( !db->autoCommit ){ sqlite4_snprintf(sizeof(zErr), zErr, "cannot DETACH database within transaction"); goto detach_error; } - if( sqlite4BtreeIsInReadTrans(pDb->pBt) || sqlite4BtreeIsInBackup(pDb->pBt) ){ + if( pDb->pKV->iTransLevel ){ sqlite4_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); goto detach_error; } - sqlite4BtreeClose(pDb->pBt); - pDb->pBt = 0; sqlite4KVStoreClose(pDb->pKV); pDb->pKV = 0; pDb->pSchema = 0; sqlite4ResetInternalSchema(db, -1); return; DELETED src/backup.c Index: src/backup.c ================================================================== --- src/backup.c +++ /dev/null @@ -1,719 +0,0 @@ -/* -** 2009 January 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 the implementation of the sqlite4_backup_XXX() -** API functions and the related features. -*/ -#include "sqliteInt.h" -#include "btreeInt.h" - -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* -** Structure allocated for each backup operation. -*/ -struct sqlite4_backup { - sqlite4* pDestDb; /* Destination database handle */ - Btree *pDest; /* Destination b-tree file */ - u32 iDestSchema; /* Original schema cookie in destination */ - int bDestLocked; /* True once a write-transaction is open on pDest */ - - Pgno iNext; /* Page number of the next source page to copy */ - sqlite4* pSrcDb; /* Source database handle */ - Btree *pSrc; /* Source b-tree file */ - - int rc; /* Backup process error code */ - - /* These two variables are set by every call to backup_step(). They are - ** read by calls to backup_remaining() and backup_pagecount(). - */ - Pgno nRemaining; /* Number of pages left to copy */ - Pgno nPagecount; /* Total number of pages to copy */ - - int isAttached; /* True once backup has been registered with pager */ - sqlite4_backup *pNext; /* Next backup associated with source pager */ -}; - -/* -** THREAD SAFETY NOTES: -** -** Once it has been created using backup_init(), a single sqlite4_backup -** structure may be accessed via two groups of thread-safe entry points: -** -** * Via the sqlite4_backup_XXX() API function backup_step() and -** backup_finish(). Both these functions obtain the source database -** handle mutex and the mutex associated with the source BtShared -** structure, in that order. -** -** * Via the BackupUpdate() and BackupRestart() functions, which are -** invoked by the pager layer to report various state changes in -** the page cache associated with the source database. The mutex -** associated with the source database BtShared structure will always -** be held when either of these functions are invoked. -** -** The other sqlite4_backup_XXX() API functions, backup_remaining() and -** backup_pagecount() are not thread-safe functions. If they are called -** while some other thread is calling backup_step() or backup_finish(), -** the values returned may be invalid. There is no way for a call to -** BackupUpdate() or BackupRestart() to interfere with backup_remaining() -** or backup_pagecount(). -** -** Depending on the SQLite configuration, the database handles and/or -** the Btree objects may have their own mutexes that require locking. -** Non-sharable Btrees (in-memory databases for example), do not have -** associated mutexes. -*/ - -/* -** Return a pointer corresponding to database zDb (i.e. "main", "temp") -** in connection handle pDb. If such a database cannot be found, return -** a NULL pointer and write an error message to pErrorDb. -** -** If the "temp" database is requested, it may need to be opened by this -** function. If an error occurs while doing so, return 0 and write an -** error message to pErrorDb. -*/ -static Btree *findBtree(sqlite4 *pErrorDb, sqlite4 *pDb, const char *zDb){ - int i = sqlite4FindDbName(pDb, zDb); - - if( i==1 ){ - Parse *pParse; - int rc = 0; - pParse = sqlite4StackAllocZero(pErrorDb, sizeof(*pParse)); - if( pParse==0 ){ - sqlite4Error(pErrorDb, SQLITE_NOMEM, "out of memory"); - rc = SQLITE_NOMEM; - }else{ - pParse->db = pDb; - if( sqlite4OpenTempDatabase(pParse) ){ - sqlite4Error(pErrorDb, pParse->rc, "%s", pParse->zErrMsg); - rc = SQLITE_ERROR; - } - sqlite4DbFree(pErrorDb, pParse->zErrMsg); - sqlite4StackFree(pErrorDb, pParse); - } - if( rc ){ - return 0; - } - } - - if( i<0 ){ - sqlite4Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); - return 0; - } - - 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(sqlite4_backup *p){ - int rc; - rc = sqlite4BtreeSetPageSize(p->pDest,sqlite4BtreeGetPageSize(p->pSrc),-1,0); - return rc; -} - -/* -** Create an sqlite4_backup process to copy the contents of zSrcDb from -** connection handle pSrcDb to zDestDb in pDestDb. If successful, return -** a pointer to the new sqlite4_backup object. -** -** If an error occurs, NULL is returned and an error code and error message -** stored in database handle pDestDb. -*/ -sqlite4_backup *sqlite4_backup_init( - sqlite4* pDestDb, /* Database to write to */ - const char *zDestDb, /* Name of database within pDestDb */ - sqlite4* pSrcDb, /* Database connection to read from */ - const char *zSrcDb /* Name of database within pSrcDb */ -){ - sqlite4_backup *p; /* Value to return */ - - /* Lock the source database handle. The destination database - ** handle is not locked in this routine, but it is locked in - ** sqlite4_backup_step(). The user is required to ensure that no - ** other thread accesses the destination handle for the duration - ** of the backup operation. Any attempt to use the destination - ** database connection while a backup is in progress may cause - ** a malfunction or a deadlock. - */ - sqlite4_mutex_enter(pSrcDb->mutex); - sqlite4_mutex_enter(pDestDb->mutex); - - if( pSrcDb==pDestDb ){ - sqlite4Error( - pDestDb, SQLITE_ERROR, "source and destination must be distinct" - ); - p = 0; - }else { - /* Allocate space for a new sqlite4_backup object... - ** EVIDENCE-OF: R-64852-21591 The sqlite4_backup object is created by a - ** call to sqlite4_backup_init() and is destroyed by a call to - ** sqlite4_backup_finish(). */ - p = (sqlite4_backup *)sqlite4_malloc(sizeof(sqlite4_backup)); - if( !p ){ - sqlite4Error(pDestDb, SQLITE_NOMEM, 0); - } - } - - /* If the allocation succeeded, populate the new object. */ - if( p ){ - memset(p, 0, sizeof(sqlite4_backup)); - p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); - p->pDest = findBtree(pDestDb, pDestDb, zDestDb); - p->pDestDb = pDestDb; - p->pSrcDb = pSrcDb; - p->iNext = 1; - p->isAttached = 0; - - 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 - ** sqlite4_backup structure. - */ - sqlite4_free(p); - p = 0; - } - } - if( p ){ - p->pSrc->nBackup++; - } - - sqlite4_mutex_leave(pDestDb->mutex); - sqlite4_mutex_leave(pSrcDb->mutex); - return p; -} - -/* -** Argument rc is an SQLite error code. Return true if this error is -** considered fatal if encountered during a backup operation. All errors -** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED. -*/ -static int isFatalError(int rc){ - return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && ALWAYS(rc!=SQLITE_LOCKED)); -} - -/* -** Parameter zSrcData points to a buffer containing the data for -** page iSrcPg from the source database. Copy this data into the -** destination database. -*/ -static int backupOnePage(sqlite4_backup *p, Pgno iSrcPg, const u8 *zSrcData){ - Pager * const pDestPager = sqlite4BtreePager(p->pDest); - const int nSrcPgsz = sqlite4BtreeGetPageSize(p->pSrc); - int nDestPgsz = sqlite4BtreeGetPageSize(p->pDest); - const int nCopy = MIN(nSrcPgsz, nDestPgsz); - const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; -#ifdef SQLITE_HAS_CODEC - int nSrcReserve = sqlite4BtreeGetReserve(p->pSrc); - int nDestReserve = sqlite4BtreeGetReserve(p->pDest); -#endif - - int rc = SQLITE_OK; - i64 iOff; - - assert( p->bDestLocked ); - assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); - assert( zSrcData ); - - /* Catch the case where the destination is an in-memory database and the - ** page sizes of the source and destination differ. - */ - if( nSrcPgsz!=nDestPgsz && sqlite4PagerIsMemdb(pDestPager) ){ - rc = SQLITE_READONLY; - } - -#ifdef SQLITE_HAS_CODEC - /* Backup is not possible if the page size of the destination is changing - ** and a codec is in use. - */ - if( nSrcPgsz!=nDestPgsz && sqlite4PagerGetCodec(pDestPager)!=0 ){ - rc = SQLITE_READONLY; - } - - /* Backup is not possible if the number of bytes of reserve space differ - ** between source and destination. If there is a difference, try to - ** fix the destination to agree with the source. If that is not possible, - ** then the backup cannot proceed. - */ - if( nSrcReserve!=nDestReserve ){ - u32 newPgsz = nSrcPgsz; - rc = sqlite4PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); - if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY; - } -#endif - - /* This loop runs once for each destination page spanned by the source - ** page. For each iteration, variable iOff is set to the byte offset - ** of the destination page. - */ - for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpDest->pBt) ) continue; - if( SQLITE_OK==(rc = sqlite4PagerGet(pDestPager, iDest, &pDestPg)) - && SQLITE_OK==(rc = sqlite4PagerWrite(pDestPg)) - ){ - const u8 *zIn = &zSrcData[iOff%nSrcPgsz]; - u8 *zDestData = sqlite4PagerGetData(pDestPg); - u8 *zOut = &zDestData[iOff%nDestPgsz]; - - /* Copy the data from the source page into the destination page. - ** Then clear the Btree layer MemPage.isInit flag. Both this module - ** and the pager code use this trick (clearing the first byte - ** of the page 'extra' space to invalidate the Btree layers - ** cached parse of the page). MemPage.isInit is marked - ** "MUST BE FIRST" for this purpose. - */ - memcpy(zOut, zIn, nCopy); - ((u8 *)sqlite4PagerGetExtra(pDestPg))[0] = 0; - } - sqlite4PagerUnref(pDestPg); - } - - return rc; -} - -/* -** If pFile is currently larger than iSize bytes, then truncate it to -** exactly iSize bytes. If pFile is not larger than iSize bytes, then -** this function is a no-op. -** -** Return SQLITE_OK if everything is successful, or an SQLite error -** code if an error occurs. -*/ -static int backupTruncateFile(sqlite4_file *pFile, i64 iSize){ - i64 iCurrent; - int rc = sqlite4OsFileSize(pFile, &iCurrent); - if( rc==SQLITE_OK && iCurrent>iSize ){ - rc = sqlite4OsTruncate(pFile, iSize); - } - return rc; -} - -/* -** Register this backup object with the associated source pager for -** callbacks when pages are changed or the cache invalidated. -*/ -static void attachBackupObject(sqlite4_backup *p){ - sqlite4_backup **pp; - assert( sqlite4BtreeHoldsMutex(p->pSrc) ); - pp = sqlite4PagerBackupPtr(sqlite4BtreePager(p->pSrc)); - p->pNext = *pp; - *pp = p; - p->isAttached = 1; -} - -/* -** Copy nPage pages from the source b-tree to the destination. -*/ -int sqlite4_backup_step(sqlite4_backup *p, int nPage){ - int rc; - int destMode; /* Destination journal mode */ - int pgszSrc = 0; /* Source page size */ - int pgszDest = 0; /* Destination page size */ - - sqlite4_mutex_enter(p->pSrcDb->mutex); - sqlite4BtreeEnter(p->pSrc); - if( p->pDestDb ){ - sqlite4_mutex_enter(p->pDestDb->mutex); - } - - rc = p->rc; - if( !isFatalError(rc) ){ - Pager * const pSrcPager = sqlite4BtreePager(p->pSrc); /* Source pager */ - Pager * const pDestPager = sqlite4BtreePager(p->pDest); /* Dest pager */ - int ii; /* Iterator variable */ - int nSrcPage = -1; /* Size of source db in pages */ - int bCloseTrans = 0; /* True if src db requires unlocking */ - - /* If the source pager is currently in a write-transaction, return - ** SQLITE_BUSY immediately. - */ - if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ - rc = SQLITE_BUSY; - }else{ - rc = SQLITE_OK; - } - - /* Lock the destination database, if it is not locked already. */ - if( SQLITE_OK==rc && p->bDestLocked==0 - && SQLITE_OK==(rc = sqlite4BtreeBeginTrans(p->pDest, 2)) - ){ - p->bDestLocked = 1; - sqlite4BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); - } - - /* If there is no open read-transaction on the source database, open - ** one now. If a transaction is opened here, then it will be closed - ** before this function exits. - */ - if( rc==SQLITE_OK && 0==sqlite4BtreeIsInReadTrans(p->pSrc) ){ - rc = sqlite4BtreeBeginTrans(p->pSrc, 0); - bCloseTrans = 1; - } - - /* Do not allow backup if the destination database is in WAL mode - ** and the page sizes are different between source and destination */ - pgszSrc = sqlite4BtreeGetPageSize(p->pSrc); - pgszDest = sqlite4BtreeGetPageSize(p->pDest); - destMode = sqlite4PagerGetJournalMode(sqlite4BtreePager(p->pDest)); - if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ - rc = SQLITE_READONLY; - } - - /* Now that there is a read-lock on the source database, query the - ** source pager for the number of pages in the database. - */ - nSrcPage = (int)sqlite4BtreeLastPage(p->pSrc); - assert( nSrcPage>=0 ); - for(ii=0; (nPage<0 || iiiNext<=(Pgno)nSrcPage && !rc; ii++){ - const Pgno iSrcPg = p->iNext; /* Source page number */ - if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ - DbPage *pSrcPg; /* Source page object */ - rc = sqlite4PagerGet(pSrcPager, iSrcPg, &pSrcPg); - if( rc==SQLITE_OK ){ - rc = backupOnePage(p, iSrcPg, sqlite4PagerGetData(pSrcPg)); - sqlite4PagerUnref(pSrcPg); - } - } - p->iNext++; - } - if( rc==SQLITE_OK ){ - p->nPagecount = nSrcPage; - p->nRemaining = nSrcPage+1-p->iNext; - if( p->iNext>(Pgno)nSrcPage ){ - rc = SQLITE_DONE; - }else if( !p->isAttached ){ - attachBackupObject(p); - } - } - - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - if( rc==SQLITE_DONE ){ - rc = sqlite4BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); - if( rc==SQLITE_OK ){ - if( p->pDestDb ){ - sqlite4ResetInternalSchema(p->pDestDb, -1); - } - if( destMode==PAGER_JOURNALMODE_WAL ){ - rc = sqlite4BtreeSetVersion(p->pDest, 2); - } - } - if( rc==SQLITE_OK ){ - int nDestTruncate; - /* Set nDestTruncate to the final number of pages in the destination - ** database. The complication here is that the destination page - ** size may be different to the source page size. - ** - ** If the source page size is smaller than the destination page size, - ** round up. In this case the call to sqlite4OsTruncate() below will - ** fix the size of the file. However it is important to call - ** sqlite4PagerTruncateImage() here so that any pages in the - ** destination file that lie beyond the nDestTruncate page mark are - ** journalled by PagerCommitPhaseOne() before they are destroyed - ** by the file truncation. - */ - assert( pgszSrc==sqlite4BtreeGetPageSize(p->pSrc) ); - assert( pgszDest==sqlite4BtreeGetPageSize(p->pDest) ); - if( pgszSrcpDest->pBt) ){ - nDestTruncate--; - } - }else{ - nDestTruncate = nSrcPage * (pgszSrc/pgszDest); - } - sqlite4PagerTruncateImage(pDestPager, nDestTruncate); - - if( pgszSrc= iSize || ( - nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) - && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest - )); - - /* 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 = sqlite4PagerCommitPhaseOne(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 && iOffpDest, 0)) - ){ - rc = SQLITE_DONE; - } - } - } - - /* If bCloseTrans is true, then this function opened a read transaction - ** on the source database. Close the read transaction here. There is - ** no need to check the return values of the btree methods here, as - ** "committing" a read-only transaction cannot fail. - */ - if( bCloseTrans ){ - TESTONLY( int rc2 ); - TESTONLY( rc2 = ) sqlite4BtreeCommitPhaseOne(p->pSrc, 0); - TESTONLY( rc2 |= ) sqlite4BtreeCommitPhaseTwo(p->pSrc, 0); - assert( rc2==SQLITE_OK ); - } - - if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM; - } - p->rc = rc; - } - if( p->pDestDb ){ - sqlite4_mutex_leave(p->pDestDb->mutex); - } - sqlite4BtreeLeave(p->pSrc); - sqlite4_mutex_leave(p->pSrcDb->mutex); - return rc; -} - -/* -** Release all resources associated with an sqlite4_backup* handle. -*/ -int sqlite4_backup_finish(sqlite4_backup *p){ - sqlite4_backup **pp; /* Ptr to head of pagers backup list */ - MUTEX_LOGIC( sqlite4_mutex *mutex; ) /* Mutex to protect source database */ - int rc; /* Value to return */ - - /* Enter the mutexes */ - if( p==0 ) return SQLITE_OK; - sqlite4_mutex_enter(p->pSrcDb->mutex); - sqlite4BtreeEnter(p->pSrc); - MUTEX_LOGIC( mutex = p->pSrcDb->mutex; ) - if( p->pDestDb ){ - sqlite4_mutex_enter(p->pDestDb->mutex); - } - - /* Detach this backup from the source pager. */ - if( p->pDestDb ){ - p->pSrc->nBackup--; - } - if( p->isAttached ){ - pp = sqlite4PagerBackupPtr(sqlite4BtreePager(p->pSrc)); - while( *pp!=p ){ - pp = &(*pp)->pNext; - } - *pp = p->pNext; - } - - /* If a transaction is still open on the Btree, roll it back. */ - sqlite4BtreeRollback(p->pDest); - - /* Set the error code of the destination database handle. */ - rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; - sqlite4Error(p->pDestDb, rc, 0); - - /* Exit the mutexes and free the backup context structure. */ - if( p->pDestDb ){ - sqlite4_mutex_leave(p->pDestDb->mutex); - } - sqlite4BtreeLeave(p->pSrc); - if( p->pDestDb ){ - /* EVIDENCE-OF: R-64852-21591 The sqlite4_backup object is created by a - ** call to sqlite4_backup_init() and is destroyed by a call to - ** sqlite4_backup_finish(). */ - sqlite4_free(p); - } - sqlite4_mutex_leave(mutex); - return rc; -} - -/* -** Return the number of pages still to be backed up as of the most recent -** call to sqlite4_backup_step(). -*/ -int sqlite4_backup_remaining(sqlite4_backup *p){ - return p->nRemaining; -} - -/* -** Return the total number of pages in the source database as of the most -** recent call to sqlite4_backup_step(). -*/ -int sqlite4_backup_pagecount(sqlite4_backup *p){ - return p->nPagecount; -} - -/* -** This function is called after the contents of page iPage of the -** source database have been modified. If page iPage has already been -** copied into the destination database, then the data written to the -** destination is now invalidated. The destination copy of iPage needs -** to be updated with the new data before the backup operation is -** complete. -** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. -*/ -void sqlite4BackupUpdate(sqlite4_backup *pBackup, Pgno iPage, const u8 *aData){ - sqlite4_backup *p; /* Iterator variable */ - for(p=pBackup; p; p=p->pNext){ - assert( sqlite4_mutex_held(p->pSrc->pBt->mutex) ); - if( !isFatalError(p->rc) && iPageiNext ){ - /* The backup process p has already copied page iPage. But now it - ** has been modified by a transaction on the source pager. Copy - ** the new data into the backup. - */ - int rc; - assert( p->pDestDb ); - sqlite4_mutex_enter(p->pDestDb->mutex); - rc = backupOnePage(p, iPage, aData); - sqlite4_mutex_leave(p->pDestDb->mutex); - assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); - if( rc!=SQLITE_OK ){ - p->rc = rc; - } - } - } -} - -/* -** Restart the backup process. This is called when the pager layer -** detects that the database has been modified by an external database -** connection. In this case there is no way of knowing which of the -** pages that have been copied into the destination database are still -** valid and which are not, so the entire process needs to be restarted. -** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. -*/ -void sqlite4BackupRestart(sqlite4_backup *pBackup){ - sqlite4_backup *p; /* Iterator variable */ - for(p=pBackup; p; p=p->pNext){ - assert( sqlite4_mutex_held(p->pSrc->pBt->mutex) ); - p->iNext = 1; - } -} - -#ifndef SQLITE_OMIT_VACUUM -/* -** Copy the complete content of pBtFrom into pBtTo. A transaction -** must be active for both files. -** -** The size of file pTo may be reduced by this operation. If anything -** goes wrong, the transaction on pTo is rolled back. If successful, the -** transaction is committed before returning. -*/ -int sqlite4BtreeCopyFile(Btree *pTo, Btree *pFrom){ - int rc; - sqlite4_file *pFd; /* File descriptor for database pTo */ - sqlite4_backup b; - sqlite4BtreeEnter(pTo); - sqlite4BtreeEnter(pFrom); - - assert( sqlite4BtreeIsInTrans(pTo) ); - pFd = sqlite4PagerFile(sqlite4BtreePager(pTo)); - if( pFd->pMethods ){ - i64 nByte = sqlite4BtreeGetPageSize(pFrom)*(i64)sqlite4BtreeLastPage(pFrom); - rc = sqlite4OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - if( rc ) goto copy_finished; - } - - /* Set up an sqlite4_backup object. sqlite4_backup.pDestDb must be set - ** to 0. This is used by the implementations of sqlite4_backup_step() - ** and sqlite4_backup_finish() to detect that they are being called - ** from this function, not directly by the user. - */ - memset(&b, 0, sizeof(b)); - b.pSrcDb = pFrom->db; - b.pSrc = pFrom; - b.pDest = pTo; - b.iNext = 1; - - /* 0x7FFFFFFF is the hard limit for the number of pages in a database - ** file. By passing this as the number of pages to copy to - ** sqlite4_backup_step(), we can guarantee that the copy finishes - ** within a single call (unless an error occurs). The assert() statement - ** checks this assumption - (p->rc) should be set to either SQLITE_DONE - ** or an error code. - */ - sqlite4_backup_step(&b, 0x7FFFFFFF); - assert( b.rc!=SQLITE_OK ); - rc = sqlite4_backup_finish(&b); - if( rc==SQLITE_OK ){ - pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; - }else{ - sqlite4PagerClearCache(sqlite4BtreePager(b.pDest)); - } - - assert( sqlite4BtreeIsInTrans(pTo)==0 ); -copy_finished: - sqlite4BtreeLeave(pFrom); - sqlite4BtreeLeave(pTo); - return rc; -} -#endif /* SQLITE_OMIT_VACUUM */ DELETED src/btmutex.c Index: src/btmutex.c ================================================================== --- src/btmutex.c +++ /dev/null @@ -1,287 +0,0 @@ -/* -** 2007 August 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code used to implement mutexes on Btree objects. -** This code really belongs in btree.c. But btree.c is getting too -** big and we want to break it down some. This packaged seemed like -** a good breakout. -*/ -#include "btreeInt.h" -#ifndef SQLITE_OMIT_SHARED_CACHE -#if SQLITE_THREADSAFE - -/* -** Obtain the BtShared mutex associated with B-Tree handle p. Also, -** set BtShared.db to the database handle associated with p and the -** p->locked boolean to true. -*/ -static void lockBtreeMutex(Btree *p){ - assert( p->locked==0 ); - assert( sqlite4_mutex_notheld(p->pBt->mutex) ); - assert( sqlite4_mutex_held(p->db->mutex) ); - - sqlite4_mutex_enter(p->pBt->mutex); - p->pBt->db = p->db; - p->locked = 1; -} - -/* -** Release the BtShared mutex associated with B-Tree handle p and -** clear the p->locked boolean. -*/ -static void unlockBtreeMutex(Btree *p){ - BtShared *pBt = p->pBt; - assert( p->locked==1 ); - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( sqlite4_mutex_held(p->db->mutex) ); - assert( p->db==pBt->db ); - - sqlite4_mutex_leave(pBt->mutex); - p->locked = 0; -} - -/* -** Enter a mutex on the given BTree object. -** -** If the object is not sharable, then no mutex is ever required -** and this routine is a no-op. The underlying mutex is non-recursive. -** But we keep a reference count in Btree.wantToLock so the behavior -** of this interface is recursive. -** -** To avoid deadlocks, multiple Btrees are locked in the same order -** by all database connections. The p->pNext is a list of other -** Btrees belonging to the same database connection as the p Btree -** which need to be locked after p. If we cannot get a lock on -** p, then first unlock all of the others on p->pNext, then wait -** for the lock to become available on p, then relock all of the -** subsequent Btrees that desire a lock. -*/ -void sqlite4BtreeEnter(Btree *p){ - Btree *pLater; - - /* Some basic sanity checking on the Btree. The list of Btrees - ** connected by pNext and pPrev should be in sorted order by - ** Btree.pBt value. All elements of the list should belong to - ** the same connection. Only shared Btrees are on the list. */ - assert( p->pNext==0 || p->pNext->pBt>p->pBt ); - assert( p->pPrev==0 || p->pPrev->pBtpBt ); - assert( p->pNext==0 || p->pNext->db==p->db ); - assert( p->pPrev==0 || p->pPrev->db==p->db ); - assert( p->sharable || (p->pNext==0 && p->pPrev==0) ); - - /* Check for locking consistency */ - assert( !p->locked || p->wantToLock>0 ); - assert( p->sharable || p->wantToLock==0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite4_mutex_held(p->db->mutex) ); - - /* Unless the database is sharable and unlocked, then BtShared.db - ** should already be set correctly. */ - assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); - - if( !p->sharable ) return; - p->wantToLock++; - if( p->locked ) return; - - /* In most cases, we should be able to acquire the lock we - ** want without having to go throught the ascending lock - ** procedure that follows. Just be sure not to block. - */ - if( sqlite4_mutex_try(p->pBt->mutex)==SQLITE_OK ){ - p->pBt->db = p->db; - p->locked = 1; - return; - } - - /* To avoid deadlock, first release all locks with a larger - ** BtShared address. Then acquire our lock. Then reacquire - ** the other BtShared locks that we used to hold in ascending - ** order. - */ - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - assert( pLater->sharable ); - assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); - assert( !pLater->locked || pLater->wantToLock>0 ); - if( pLater->locked ){ - unlockBtreeMutex(pLater); - } - } - lockBtreeMutex(p); - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - if( pLater->wantToLock ){ - lockBtreeMutex(pLater); - } - } -} - -/* -** Exit the recursive mutex on a Btree. -*/ -void sqlite4BtreeLeave(Btree *p){ - if( p->sharable ){ - assert( p->wantToLock>0 ); - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } -} - -#ifndef NDEBUG -/* -** Return true if the BtShared mutex is held on the btree, or if the -** B-Tree is not marked as sharable. -** -** This routine is used only from within assert() statements. -*/ -int sqlite4BtreeHoldsMutex(Btree *p){ - assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 ); - assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db ); - assert( p->sharable==0 || p->locked==0 || sqlite4_mutex_held(p->pBt->mutex) ); - assert( p->sharable==0 || p->locked==0 || sqlite4_mutex_held(p->db->mutex) ); - - return (p->sharable==0 || p->locked); -} -#endif - - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Enter and leave a mutex on a Btree given a cursor owned by that -** Btree. These entry points are used by incremental I/O and can be -** omitted if that module is not used. -*/ -void sqlite4BtreeEnterCursor(BtCursor *pCur){ - sqlite4BtreeEnter(pCur->pBtree); -} -void sqlite4BtreeLeaveCursor(BtCursor *pCur){ - sqlite4BtreeLeave(pCur->pBtree); -} -#endif /* SQLITE_OMIT_INCRBLOB */ - - -/* -** Enter the mutex on every Btree associated with a database -** connection. This is needed (for example) prior to parsing -** a statement since we will be comparing table and column names -** against all schemas and we do not want those schemas being -** reset out from under us. -** -** There is a corresponding leave-all procedures. -** -** Enter the mutexes in accending order by BtShared pointer address -** to avoid the possibility of deadlock when two threads with -** two or more btrees in common both try to lock all their btrees -** at the same instant. -*/ -void sqlite4BtreeEnterAll(sqlite4 *db){ - int i; - Btree *p; - assert( sqlite4_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p ) sqlite4BtreeEnter(p); - } -} -void sqlite4BtreeLeaveAll(sqlite4 *db){ - int i; - Btree *p; - assert( sqlite4_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p ) sqlite4BtreeLeave(p); - } -} - -/* -** Return true if a particular Btree requires a lock. Return FALSE if -** no lock is ever required since it is not sharable. -*/ -int sqlite4BtreeSharable(Btree *p){ - return p->sharable; -} - -#ifndef NDEBUG -/* -** Return true if the current thread holds the database connection -** mutex and all required BtShared mutexes. -** -** This routine is used inside assert() statements only. -*/ -int sqlite4BtreeHoldsAllMutexes(sqlite4 *db){ - int i; - if( !sqlite4_mutex_held(db->mutex) ){ - return 0; - } - for(i=0; inDb; i++){ - Btree *p; - p = db->aDb[i].pBt; - if( p && p->sharable && - (p->wantToLock==0 || !sqlite4_mutex_held(p->pBt->mutex)) ){ - return 0; - } - } - return 1; -} -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* -** Return true if the correct mutexes are held for accessing the -** db->aDb[iDb].pSchema structure. The mutexes required for schema -** access are: -** -** (1) The mutex on db -** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. -** -** If pSchema is not NULL, then iDb is computed from pSchema and -** db using sqlite4SchemaToIndex(). -*/ -int sqlite4SchemaMutexHeld(sqlite4 *db, int iDb, Schema *pSchema){ - Btree *p; - assert( db!=0 ); - if( pSchema ) iDb = sqlite4SchemaToIndex(db, pSchema); - assert( iDb>=0 && iDbnDb ); - if( !sqlite4_mutex_held(db->mutex) ) return 0; - if( iDb==1 ) return 1; - p = db->aDb[iDb].pBt; - assert( p!=0 ); - return p->sharable==0 || p->locked==1; -} -#endif /* NDEBUG */ - -#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ -/* -** The following are special cases for mutex enter routines for use -** in single threaded applications that use shared cache. Except for -** these two routines, all mutex operations are no-ops in that case and -** are null #defines in btree.h. -** -** If shared cache is disabled, then all btree mutex routines, including -** the ones below, are no-ops and are null #defines in btree.h. -*/ - -void sqlite4BtreeEnter(Btree *p){ - p->pBt->db = p->db; -} -void sqlite4BtreeEnterAll(sqlite4 *db){ - int i; - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p ){ - p->pBt->db = p->db; - } - } -} -#endif /* if SQLITE_THREADSAFE */ -#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ DELETED src/btree.c Index: src/btree.c ================================================================== --- src/btree.c +++ /dev/null @@ -1,8271 +0,0 @@ -/* -** 2004 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements a external (disk-based) database using BTrees. -** See the header comment on "btreeInt.h" for additional information. -** Including a description of file format and an overview of operation. -*/ -#include "btreeInt.h" - -/* -** The header string that appears at the beginning of every -** SQLite database. -*/ -static const char zMagicHeader[] = SQLITE_FILE_HEADER; - -/* -** Set this global variable to 1 to enable tracing using the TRACE -** macro. -*/ -#if 0 -int sqlite4BtreeTrace=1; /* True to enable tracing */ -# define TRACE(X) if(sqlite4BtreeTrace){printf X;fflush(stdout);} -#else -# define TRACE(X) -#endif - -/* -** Extract a 2-byte big-endian integer from an array of unsigned bytes. -** But if the value is zero, make it 65536. -** -** This routine is used to extract the "offset to cell content area" value -** from the header of a btree page. If the page size is 65536 and the page -** is empty, the offset should be 65536, but the 2-byte value stores zero. -** This routine makes the necessary adjustment to 65536. -*/ -#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** A list of BtShared objects that are eligible for participation -** in shared cache. This variable has file scope during normal builds, -** but the test harness needs to access it so we make it global for -** test builds. -** -** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER. -*/ -#ifdef SQLITE_TEST -BtShared *SQLITE_WSD sqlite4SharedCacheList = 0; -#else -static BtShared *SQLITE_WSD sqlite4SharedCacheList = 0; -#endif -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Enable or disable the shared pager and schema features. -** -** This routine has no effect on existing database connections. -** The shared cache setting effects only future calls to -** sqlite4_open(), sqlite4_open16(), or sqlite4_open_v2(). -*/ -int sqlite4_enable_shared_cache(int enable){ - sqlite4GlobalConfig.sharedCacheEnabled = enable; - return SQLITE_OK; -} -#endif - - - -#ifdef SQLITE_OMIT_SHARED_CACHE - /* - ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), - ** and clearAllSharedCacheTableLocks() - ** manipulate entries in the BtShared.pLock linked list used to store - ** shared-cache table level locks. If the library is compiled with the - ** shared-cache feature disabled, then there is only ever one user - ** of each BtShared structure and so this locking is not necessary. - ** So define the lock related functions as no-ops. - */ - #define querySharedCacheTableLock(a,b,c) SQLITE_OK - #define setSharedCacheTableLock(a,b,c) SQLITE_OK - #define clearAllSharedCacheTableLocks(a) - #define downgradeAllSharedCacheTableLocks(a) - #define hasSharedCacheTableLock(a,b,c,d) 1 - #define hasReadConflicts(a, b) 0 -#endif - -#ifndef SQLITE_OMIT_SHARED_CACHE - -#ifdef SQLITE_DEBUG -/* -**** This function is only used as part of an assert() statement. *** -** -** Check to see if pBtree holds the required locks to read or write to the -** table with root page iRoot. Return 1 if it does and 0 if not. -** -** For example, when writing to a table with root-page iRoot via -** Btree connection pBtree: -** -** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); -** -** When writing to an index that resides in a sharable database, the -** caller should have first obtained a lock specifying the root page of -** the corresponding table. This makes things a bit more complicated, -** as this module treats each table as a separate structure. To determine -** the table corresponding to the index being written, this -** function has to search through the database schema. -** -** Instead of a lock on the table/index rooted at page iRoot, the caller may -** hold a write-lock on the schema table (root page 1). This is also -** acceptable. -*/ -static int hasSharedCacheTableLock( - Btree *pBtree, /* Handle that must hold lock */ - Pgno iRoot, /* Root page of b-tree */ - int isIndex, /* True if iRoot is the root of an index b-tree */ - int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ -){ - Schema *pSchema = (Schema *)pBtree->pBt->pSchema; - Pgno iTab = 0; - BtLock *pLock; - - /* If this database is not shareable, or if the client is reading - ** and has the read-uncommitted flag set, then no lock is required. - ** Return true immediately. - */ - if( (pBtree->sharable==0) - || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) - ){ - return 1; - } - - /* If the client is reading or writing an index and the schema is - ** not loaded, then it is too difficult to actually check to see if - ** the correct locks are held. So do not bother - just return true. - ** This case does not come up very often anyhow. - */ - if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){ - return 1; - } - - /* Figure out the root-page that the lock should be held on. For table - ** b-trees, this is just the root page of the b-tree being read or - ** written. For index b-trees, it is the root page of the associated - ** table. */ - if( isIndex ){ - HashElem *p; - for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ - Index *pIdx = (Index *)sqliteHashData(p); - if( pIdx->tnum==(int)iRoot ){ - iTab = pIdx->pTable->tnum; - } - } - }else{ - iTab = iRoot; - } - - /* Search for the required lock. Either a write-lock on root-page iTab, a - ** write-lock on the schema table, or (if the client is reading) a - ** read-lock on iTab will suffice. Return 1 if any of these are found. */ - for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ - if( pLock->pBtree==pBtree - && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) - && pLock->eLock>=eLockType - ){ - return 1; - } - } - - /* Failed to find the required lock. */ - return 0; -} -#endif /* SQLITE_DEBUG */ - -#ifdef SQLITE_DEBUG -/* -**** This function may be used as part of assert() statements only. **** -** -** Return true if it would be illegal for pBtree to write into the -** table or index rooted at iRoot because other shared connections are -** simultaneously reading that same table or index. -** -** It is illegal for pBtree to write if some other Btree object that -** shares the same BtShared object is currently reading or writing -** the iRoot table. Except, if the other Btree object has the -** read-uncommitted flag set, then it is OK for the other object to -** have a read cursor. -** -** For example, before writing to any part of the table or index -** rooted at page iRoot, one should call: -** -** assert( !hasReadConflicts(pBtree, iRoot) ); -*/ -static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ - BtCursor *p; - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==iRoot - && p->pBtree!=pBtree - && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted) - ){ - return 1; - } - } - return 0; -} -#endif /* #ifdef SQLITE_DEBUG */ - -/* -** Query to see if Btree handle p may obtain a lock of type eLock -** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return -** SQLITE_OK if the lock may be obtained (by calling -** setSharedCacheTableLock()), or SQLITE_LOCKED if not. -*/ -static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pIter; - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - assert( !(p->db->flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 ); - - /* If requesting a write-lock, then the Btree must have an open write - ** transaction on this file. And, obviously, for this to be so there - ** must be an open write transaction on the file itself. - */ - assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); - assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); - - /* This routine is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ - return SQLITE_OK; - } - - /* If some other connection is holding an exclusive lock, the - ** requested lock may not be obtained. - */ - if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){ - sqlite4ConnectionBlocked(p->db, pBt->pWriter->db); - return SQLITE_LOCKED_SHAREDCACHE; - } - - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlite4ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->btsFlags |= BTS_PENDING; - } - return SQLITE_LOCKED_SHAREDCACHE; - } - } - return SQLITE_OK; -} -#endif /* !SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Add a lock on the table with root-page iTable to the shared-btree used -** by Btree handle p. Parameter eLock must be either READ_LOCK or -** WRITE_LOCK. -** -** This function assumes the following: -** -** (a) The specified Btree object p is connected to a sharable -** database (one with the BtShared.sharable flag set), and -** -** (b) No other Btree objects hold a lock that conflicts -** with the requested lock (i.e. querySharedCacheTableLock() has -** already been called and returned SQLITE_OK). -** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM -** is returned if a malloc attempt fails. -*/ -static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pLock = 0; - BtLock *pIter; - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - - /* A connection with the read-uncommitted flag set will never try to - ** obtain a read-lock using this function. The only read-lock obtained - ** by a connection in read-uncommitted mode is on the sqlite_master - ** table, and that lock is obtained in BtreeBeginTrans(). */ - assert( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK ); - - /* This function should only be called on a sharable b-tree after it - ** has been determined that no other b-tree holds a conflicting lock. */ - assert( p->sharable ); - assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - - /* First search the list for an existing lock on this table. */ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->iTable==iTable && pIter->pBtree==p ){ - pLock = pIter; - break; - } - } - - /* If the above search did not find a BtLock struct associating Btree p - ** with table iTable, allocate one and link it into the list. - */ - if( !pLock ){ - pLock = (BtLock *)sqlite4MallocZero(sizeof(BtLock)); - if( !pLock ){ - return SQLITE_NOMEM; - } - pLock->iTable = iTable; - pLock->pBtree = p; - pLock->pNext = pBt->pLock; - pBt->pLock = pLock; - } - - /* Set the BtLock.eLock variable to the maximum of the current lock - ** and the requested lock. This means if a write-lock was already held - ** and a read-lock requested, we don't incorrectly downgrade the lock. - */ - assert( WRITE_LOCK>READ_LOCK ); - if( eLock>pLock->eLock ){ - pLock->eLock = eLock; - } - - return SQLITE_OK; -} -#endif /* !SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree object p. -** -** This function assumes that Btree p has an open read or write -** transaction. If it does not, then the BTS_PENDING flag -** may be incorrectly cleared. -*/ -static void clearAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - BtLock **ppIter = &pBt->pLock; - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( p->sharable || 0==*ppIter ); - assert( p->inTrans>0 ); - - while( *ppIter ){ - BtLock *pLock = *ppIter; - assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); - assert( pLock->pBtree->inTrans>=pLock->eLock ); - if( pLock->pBtree==p ){ - *ppIter = pLock->pNext; - assert( pLock->iTable!=1 || pLock==&p->lock ); - if( pLock->iTable!=1 ){ - sqlite4_free(pLock); - } - }else{ - ppIter = &pLock->pNext; - } - } - - assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter ); - if( pBt->pWriter==p ){ - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - }else if( pBt->nTransaction==2 ){ - /* This function is called when Btree p is concluding its - ** transaction. If there currently exists a writer, and p is not - ** that writer, then the number of locks held by connections other - ** than the writer must be about to drop to zero. In this case - ** set the BTS_PENDING flag to 0. - ** - ** If there is not currently a writer, then BTS_PENDING must - ** be zero already. So this next line is harmless in that case. - */ - pBt->btsFlags &= ~BTS_PENDING; - } -} - -/* -** This function changes all write-locks held by Btree p into read-locks. -*/ -static void downgradeAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - if( pBt->pWriter==p ){ - BtLock *pLock; - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ - assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); - pLock->eLock = READ_LOCK; - } - } -} - -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -static void releasePage(MemPage *pPage); /* Forward reference */ - -/* -***** This routine is used inside of assert() only **** -** -** Verify that the cursor holds the mutex on its BtShared -*/ -#ifdef SQLITE_DEBUG -static int cursorHoldsMutex(BtCursor *p){ - return sqlite4_mutex_held(p->pBt->mutex); -} -#endif - - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Invalidate the overflow page-list cache for cursor pCur, if any. -*/ -static void invalidateOverflowCache(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlite4_free(pCur->aOverflow); - pCur->aOverflow = 0; -} - -/* -** Invalidate the overflow page-list cache for all cursors opened -** on the shared btree structure pBt. -*/ -static void invalidateAllOverflowCache(BtShared *pBt){ - BtCursor *p; - assert( sqlite4_mutex_held(pBt->mutex) ); - for(p=pBt->pCursor; p; p=p->pNext){ - invalidateOverflowCache(p); - } -} - -/* -** This function is called before modifying the contents of a table -** to invalidate any incrblob cursors that are open on the -** row or one of the rows being modified. -** -** If argument isClearTable is true, then the entire contents of the -** table is about to be deleted. In this case invalidate all incrblob -** cursors open on any row within the table with root-page pgnoRoot. -** -** Otherwise, if argument isClearTable is false, then the row with -** rowid iRow is being replaced or deleted. In this case invalidate -** only those incrblob cursors open on that specific row. -*/ -static void invalidateIncrblobCursors( - Btree *pBtree, /* The database file to check */ - i64 iRow, /* The rowid that might be changing */ - int isClearTable /* True if all rows are being deleted */ -){ - BtCursor *p; - BtShared *pBt = pBtree->pBt; - assert( sqlite4BtreeHoldsMutex(pBtree) ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ - p->eState = CURSOR_INVALID; - } - } -} - -#else - /* Stub functions when INCRBLOB is omitted */ - #define invalidateOverflowCache(x) - #define invalidateAllOverflowCache(x) - #define invalidateIncrblobCursors(x,y,z) -#endif /* SQLITE_OMIT_INCRBLOB */ - -/* -** Set bit pgno of the BtShared.pHasContent bitvec. This is called -** when a page that previously contained data becomes a free-list leaf -** page. -** -** The BtShared.pHasContent bitvec exists to work around an obscure -** bug caused by the interaction of two useful IO optimizations surrounding -** free-list leaf pages: -** -** 1) When all data is deleted from a page and the page becomes -** a free-list leaf page, the page is not written to the database -** (as free-list leaf pages contain no meaningful data). Sometimes -** such a page is not even journalled (as it will not be modified, -** why bother journalling it?). -** -** 2) When a free-list leaf page is reused, its content is not read -** from the database or written to the journal file (why should it -** be, if it is not at all meaningful?). -** -** By themselves, these optimizations work fine and provide a handy -** performance boost to bulk delete or insert operations. However, if -** a page is moved to the free-list and then reused within the same -** transaction, a problem comes up. If the page is not journalled when -** it is moved to the free-list and it is also not journalled when it -** is extracted from the free-list and reused, then the original data -** may be lost. In the event of a rollback, it may not be possible -** to restore the database to its original configuration. -** -** The solution is the BtShared.pHasContent bitvec. Whenever a page is -** moved to become a free-list leaf page, the corresponding bit is -** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is omitted if the corresponding bit is already -** set in BtShared.pHasContent. The contents of the bitvec are cleared -** at the end of every transaction. -*/ -static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ - int rc = SQLITE_OK; - if( !pBt->pHasContent ){ - assert( pgno<=pBt->nPage ); - pBt->pHasContent = sqlite4BitvecCreate(pBt->nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; - } - } - if( rc==SQLITE_OK && pgno<=sqlite4BitvecSize(pBt->pHasContent) ){ - rc = sqlite4BitvecSet(pBt->pHasContent, pgno); - } - return rc; -} - -/* -** Query the BtShared.pHasContent vector. -** -** This function is called when a free-list leaf page is removed from the -** free-list for reuse. It returns false if it is safe to retrieve the -** page from the pager layer with the 'no-content' flag set. True otherwise. -*/ -static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ - Bitvec *p = pBt->pHasContent; - return (p && (pgno>sqlite4BitvecSize(p) || sqlite4BitvecTest(p, pgno))); -} - -/* -** Clear (destroy) the BtShared.pHasContent bitvec. This should be -** invoked at the conclusion of each write-transaction. -*/ -static void btreeClearHasContent(BtShared *pBt){ - sqlite4BitvecDestroy(pBt->pHasContent); - pBt->pHasContent = 0; -} - -/* -** Save the current cursor position in the variables BtCursor.nKey -** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. -** -** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) -** prior to calling this routine. -*/ -static int saveCursorPosition(BtCursor *pCur){ - int rc; - - assert( CURSOR_VALID==pCur->eState ); - assert( 0==pCur->pKey ); - assert( cursorHoldsMutex(pCur) ); - - rc = sqlite4BtreeKeySize(pCur, &pCur->nKey); - assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ - - /* If this is an intKey table, then the above call to BtreeKeySize() - ** stores the integer key in pCur->nKey. In this case this value is - ** all that is required. Otherwise, if pCur is not open on an intKey - ** table, then malloc space for and store the pCur->nKey bytes of key - ** data. - */ - if( 0==pCur->apPage[0]->intKey ){ - void *pKey = sqlite4Malloc( (int)pCur->nKey ); - if( pKey ){ - rc = sqlite4BtreeKey(pCur, 0, (int)pCur->nKey, pKey); - if( rc==SQLITE_OK ){ - pCur->pKey = pKey; - }else{ - sqlite4_free(pKey); - } - }else{ - rc = SQLITE_NOMEM; - } - } - assert( !pCur->apPage[0]->intKey || !pCur->pKey ); - - if( rc==SQLITE_OK ){ - int i; - for(i=0; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - pCur->apPage[i] = 0; - } - pCur->iPage = -1; - pCur->eState = CURSOR_REQUIRESEEK; - } - - invalidateOverflowCache(pCur); - return rc; -} - -/* -** Save the positions of all cursors (except pExcept) that are open on -** the table with root-page iRoot. Usually, this is called just before cursor -** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). -*/ -static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ - BtCursor *p; - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( pExcept==0 || pExcept->pBt==pBt ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && - p->eState==CURSOR_VALID ){ - int rc = saveCursorPosition(p); - if( SQLITE_OK!=rc ){ - return rc; - } - } - } - return SQLITE_OK; -} - -/* -** Clear the current cursor position. -*/ -void sqlite4BtreeClearCursor(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlite4_free(pCur->pKey); - pCur->pKey = 0; - pCur->eState = CURSOR_INVALID; -} - -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -static int btreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ - char *pFree = 0; - - if( pKey ){ - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite4VdbeAllocUnpackedRecord( - pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree - ); - if( pIdxKey==0 ) return SQLITE_NOMEM; - sqlite4VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); - }else{ - pIdxKey = 0; - } - rc = sqlite4BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); - if( pFree ){ - sqlite4DbFree(pCur->pKeyInfo->db, pFree); - } - return rc; -} - -/* -** Restore the cursor to the position it was in (or as close to as possible) -** when saveCursorPosition() was called. Note that this call deletes the -** saved position info stored by saveCursorPosition(), so there can be -** at most one effective restoreCursorPosition() call after each -** saveCursorPosition(). -*/ -static int btreeRestoreCursorPosition(BtCursor *pCur){ - int rc; - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState>=CURSOR_REQUIRESEEK ); - if( pCur->eState==CURSOR_FAULT ){ - return pCur->skipNext; - } - pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); - if( rc==SQLITE_OK ){ - sqlite4_free(pCur->pKey); - pCur->pKey = 0; - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); - } - return rc; -} - -#define restoreCursorPosition(p) \ - (p->eState>=CURSOR_REQUIRESEEK ? \ - btreeRestoreCursorPosition(p) : \ - SQLITE_OK) - -/* -** Determine whether or not a cursor has moved from the position it -** was last placed at. Cursors can move when the row they are pointing -** at is deleted out from under them. -** -** This routine returns an error code if something goes wrong. The -** integer *pHasMoved is set to one if the cursor has moved and 0 if not. -*/ -int sqlite4BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){ - int rc; - - rc = restoreCursorPosition(pCur); - if( rc ){ - *pHasMoved = 1; - return rc; - } - if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){ - *pHasMoved = 1; - }else{ - *pHasMoved = 0; - } - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Given a page number of a regular database page, return the page -** number for the pointer-map page that contains the entry for the -** input page number. -** -** Return 0 (not a valid page) for pgno==1 since there is -** no pointer map associated with page 1. The integrity_check logic -** requires that ptrmapPageno(*,1)!=1. -*/ -static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ - int nPagesPerMapPage; - Pgno iPtrMap, ret; - assert( sqlite4_mutex_held(pBt->mutex) ); - if( pgno<2 ) return 0; - nPagesPerMapPage = (pBt->usableSize/5)+1; - iPtrMap = (pgno-2)/nPagesPerMapPage; - ret = (iPtrMap*nPagesPerMapPage) + 2; - if( ret==PENDING_BYTE_PAGE(pBt) ){ - ret++; - } - return ret; -} - -/* -** Write an entry into the pointer map. -** -** This routine updates the pointer map entry for page number 'key' -** so that it maps to type 'eType' and parent page number 'pgno'. -** -** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is -** a no-op. If an error occurs, the appropriate error code is written -** into *pRC. -*/ -static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ - DbPage *pDbPage; /* The pointer map page */ - u8 *pPtrmap; /* The pointer map data */ - Pgno iPtrmap; /* The pointer map page number */ - int offset; /* Offset in pointer map page */ - int rc; /* Return code from subfunctions */ - - if( *pRC ) return; - - assert( sqlite4_mutex_held(pBt->mutex) ); - /* The master-journal page number must never be used as a pointer map page */ - assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); - - assert( pBt->autoVacuum ); - if( key==0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlite4PagerGet(pBt->pPager, iPtrmap, &pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - goto ptrmap_exit; - } - assert( offset <= (int)pBt->usableSize-5 ); - pPtrmap = (u8 *)sqlite4PagerGetData(pDbPage); - - if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ - TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - *pRC= rc = sqlite4PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - pPtrmap[offset] = eType; - put4byte(&pPtrmap[offset+1], parent); - } - } - -ptrmap_exit: - sqlite4PagerUnref(pDbPage); -} - -/* -** Read an entry from the pointer map. -** -** This routine retrieves the pointer map entry for page 'key', writing -** the type and parent page number to *pEType and *pPgno respectively. -** An error code is returned if something goes wrong, otherwise SQLITE_OK. -*/ -static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ - DbPage *pDbPage; /* The pointer map page */ - int iPtrmap; /* Pointer map page index */ - u8 *pPtrmap; /* Pointer map page data */ - int offset; /* Offset of entry in pointer map */ - int rc; - - assert( sqlite4_mutex_held(pBt->mutex) ); - - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlite4PagerGet(pBt->pPager, iPtrmap, &pDbPage); - if( rc!=0 ){ - return rc; - } - pPtrmap = (u8 *)sqlite4PagerGetData(pDbPage); - - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - sqlite4PagerUnref(pDbPage); - return SQLITE_CORRUPT_BKPT; - } - assert( offset <= (int)pBt->usableSize-5 ); - assert( pEType!=0 ); - *pEType = pPtrmap[offset]; - if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); - - sqlite4PagerUnref(pDbPage); - if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT; - return SQLITE_OK; -} - -#else /* if defined SQLITE_OMIT_AUTOVACUUM */ - #define ptrmapPut(w,x,y,z,rc) - #define ptrmapGet(w,x,y,z) SQLITE_OK - #define ptrmapPutOvflPtr(x, y, rc) -#endif - -/* -** Given a btree page and a cell index (0 means the first cell on -** the page, 1 means the second cell, and so forth) return a pointer -** to the cell content. -** -** This routine works only for pages that do not contain overflow cells. -*/ -#define findCell(P,I) \ - ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)]))) -#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I))))) - - -/* -** This a more complex version of findCell() that works for -** pages that do contain overflow cells. -*/ -static u8 *findOverflowCell(MemPage *pPage, int iCell){ - int i; - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - for(i=pPage->nOverflow-1; i>=0; i--){ - int k; - struct _OvflCell *pOvfl; - pOvfl = &pPage->aOvfl[i]; - k = pOvfl->idx; - if( k<=iCell ){ - if( k==iCell ){ - return pOvfl->pCell; - } - iCell--; - } - } - return findCell(pPage, iCell); -} - -/* -** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. btreeParseCell() takes a -** cell index as the second argument and btreeParseCellPtr() -** takes a pointer to the body of the cell as its second argument. -** -** Within this file, the parseCell() macro can be called instead of -** btreeParseCellPtr(). Using some compilers, this will be faster. -*/ -static void btreeParseCellPtr( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - u16 n; /* Number bytes in cell content header */ - u32 nPayload; /* Number of bytes of cell payload */ - - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - - pInfo->pCell = pCell; - assert( pPage->leaf==0 || pPage->leaf==1 ); - n = pPage->childPtrSize; - assert( n==4-4*pPage->leaf ); - if( pPage->intKey ){ - if( pPage->hasData ){ - n += getVarint32(&pCell[n], nPayload); - }else{ - nPayload = 0; - } - n += getVarint(&pCell[n], (u64*)&pInfo->nKey); - pInfo->nData = nPayload; - }else{ - pInfo->nData = 0; - n += getVarint32(&pCell[n], nPayload); - pInfo->nKey = nPayload; - } - pInfo->nPayload = nPayload; - pInfo->nHeader = n; - testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); - if( likely(nPayload<=pPage->maxLocal) ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ - if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - pInfo->iOverflow = 0; - }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 - ** overflow pages. The strategy is to minimize the amount of unused - ** space on overflow pages while keeping the amount of local storage - ** in between minLocal and maxLocal. - ** - ** Warning: changing the way overflow payload is distributed in any - ** way will result in an incompatible file format. - */ - int minLocal; /* Minimum amount of payload held locally */ - int maxLocal; /* Maximum amount of payload held locally */ - int surplus; /* Overflow payload available for local storage */ - - minLocal = pPage->minLocal; - maxLocal = pPage->maxLocal; - surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); - testcase( surplus==maxLocal ); - testcase( surplus==maxLocal+1 ); - if( surplus <= maxLocal ){ - pInfo->nLocal = (u16)surplus; - }else{ - pInfo->nLocal = (u16)minLocal; - } - pInfo->iOverflow = (u16)(pInfo->nLocal + n); - pInfo->nSize = pInfo->iOverflow + 4; - } -} -#define parseCell(pPage, iCell, pInfo) \ - btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) -static void btreeParseCell( - MemPage *pPage, /* Page containing the cell */ - int iCell, /* The cell index. First cell is 0 */ - CellInfo *pInfo /* Fill in this structure */ -){ - parseCell(pPage, iCell, pInfo); -} - -/* -** Compute the total number of bytes that a Cell needs in the cell -** data area of the btree-page. The return number includes the cell -** data header and the local payload, but not any overflow page or -** the space used by the cell pointer. -*/ -static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - u8 *pIter = &pCell[pPage->childPtrSize]; - u32 nSize; - -#ifdef SQLITE_DEBUG - /* The value returned by this function should always be the same as - ** the (CellInfo.nSize) value found by doing a full parse of the - ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of - ** this function verifies that this invariant is not violated. */ - CellInfo debuginfo; - btreeParseCellPtr(pPage, pCell, &debuginfo); -#endif - - if( pPage->intKey ){ - u8 *pEnd; - if( pPage->hasData ){ - pIter += getVarint32(pIter, nSize); - }else{ - nSize = 0; - } - - /* pIter now points at the 64-bit integer key value, a variable length - ** integer. The following block moves pIter to point at the first byte - ** past the end of the key value. */ - pEnd = &pIter[9]; - while( (*pIter++)&0x80 && pItermaxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - int minLocal = pPage->minLocal; - nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); - testcase( nSize==pPage->maxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - nSize = minLocal; - } - nSize += 4; - } - nSize += (u32)(pIter - pCell); - - /* The minimum size of any cell is 4 bytes. */ - if( nSize<4 ){ - nSize = 4; - } - - assert( nSize==debuginfo.nSize ); - return (u16)nSize; -} - -#ifdef SQLITE_DEBUG -/* This variation on cellSizePtr() is used inside of assert() statements -** only. */ -static u16 cellSize(MemPage *pPage, int iCell){ - return cellSizePtr(pPage, findCell(pPage, iCell)); -} -#endif - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** If the cell pCell, part of page pPage contains a pointer -** to an overflow page, insert an entry into the pointer-map -** for the overflow page. -*/ -static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ - CellInfo info; - if( *pRC ) return; - assert( pCell!=0 ); - btreeParseCellPtr(pPage, pCell, &info); - assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload ); - if( info.iOverflow ){ - Pgno ovfl = get4byte(&pCell[info.iOverflow]); - ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); - } -} -#endif - - -/* -** Defragment the page given. All Cells are moved to the -** end of the page and all free space is collected into one -** big FreeBlk that occurs in between the header and cell -** pointer array and the cell content area. -*/ -static int defragmentPage(MemPage *pPage){ - int i; /* Loop counter */ - int pc; /* Address of a i-th cell */ - int hdr; /* Offset to the page header */ - int size; /* Size of a cell */ - int usableSize; /* Number of usable bytes on a page */ - int cellOffset; /* Offset to the cell pointer array */ - int cbrk; /* Offset to the cell content area */ - int nCell; /* Number of cells on the page */ - unsigned char *data; /* The page data */ - unsigned char *temp; /* Temp area for cell content */ - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ - - - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); - assert( pPage->nOverflow==0 ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - temp = sqlite4PagerTempSpace(pPage->pBt->pPager); - data = pPage->aData; - hdr = pPage->hdrOffset; - cellOffset = pPage->cellOffset; - nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) ); - usableSize = pPage->pBt->usableSize; - cbrk = get2byte(&data[hdr+5]); - memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk); - cbrk = usableSize; - iCellFirst = cellOffset + 2*nCell; - iCellLast = usableSize - 4; - for(i=0; iiCellLast ){ - return SQLITE_CORRUPT_BKPT; - } -#endif - assert( pc>=iCellFirst && pc<=iCellLast ); - size = cellSizePtr(pPage, &temp[pc]); - cbrk -= size; -#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) - if( cbrkusableSize ){ - return SQLITE_CORRUPT_BKPT; - } -#endif - assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); - testcase( cbrk+size==usableSize ); - testcase( pc+size==usableSize ); - memcpy(&data[cbrk], &temp[pc], size); - put2byte(pAddr, cbrk); - } - assert( cbrk>=iCellFirst ); - put2byte(&data[hdr+5], cbrk); - data[hdr+1] = 0; - data[hdr+2] = 0; - data[hdr+7] = 0; - memset(&data[iCellFirst], 0, cbrk-iCellFirst); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - if( cbrk-iCellFirst!=pPage->nFree ){ - return SQLITE_CORRUPT_BKPT; - } - return SQLITE_OK; -} - -/* -** Allocate nByte bytes of space from within the B-Tree page passed -** as the first argument. Write into *pIdx the index into pPage->aData[] -** of the first byte of allocated space. Return either SQLITE_OK or -** an error code (usually SQLITE_CORRUPT). -** -** The caller guarantees that there is sufficient space to make the -** allocation. This routine might need to defragment in order to bring -** all the space together, however. This routine will avoid using -** the first two bytes past the cell pointer area since presumably this -** allocation is being made in order to insert a new cell, so we will -** also end up needing a new cell pointer. -*/ -static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ - u8 * const data = pPage->aData; /* Local cache of pPage->aData */ - int nFrag; /* Number of fragmented bytes on pPage */ - int top; /* First byte of cell content area */ - int gap; /* First byte of gap between cell pointers and cell content */ - int rc; /* Integer return code */ - int usableSize; /* Usable size of the page */ - - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( nByte>=0 ); /* Minimum cell size is 4 */ - assert( pPage->nFree>=nByte ); - assert( pPage->nOverflow==0 ); - usableSize = pPage->pBt->usableSize; - assert( nByte < usableSize-8 ); - - nFrag = data[hdr+7]; - assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); - gap = pPage->cellOffset + 2*pPage->nCell; - top = get2byteNotZero(&data[hdr+5]); - if( gap>top ) return SQLITE_CORRUPT_BKPT; - testcase( gap+2==top ); - testcase( gap+1==top ); - testcase( gap==top ); - - if( nFrag>=60 ){ - /* Always defragment highly fragmented pages */ - rc = defragmentPage(pPage); - if( rc ) return rc; - top = get2byteNotZero(&data[hdr+5]); - }else if( gap+2<=top ){ - /* Search the freelist looking for a free slot big enough to satisfy - ** the request. The allocation is made from the first free slot in - ** the list that is large enough to accomadate it. - */ - int pc, addr; - for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ - int size; /* Size of the free slot */ - if( pc>usableSize-4 || pc=nByte ){ - int x = size - nByte; - testcase( x==4 ); - testcase( x==3 ); - if( x<4 ){ - /* Remove the slot from the free-list. Update the number of - ** fragmented bytes within the page. */ - memcpy(&data[addr], &data[pc], 2); - data[hdr+7] = (u8)(nFrag + x); - }else if( size+pc > usableSize ){ - return SQLITE_CORRUPT_BKPT; - }else{ - /* The slot remains on the free-list. Reduce its size to account - ** for the portion used by the new allocation. */ - put2byte(&data[pc+2], x); - } - *pIdx = pc + x; - return SQLITE_OK; - } - } - } - - /* Check to make sure there is enough space in the gap to satisfy - ** the allocation. If not, defragment. - */ - testcase( gap+2+nByte==top ); - if( gap+2+nByte>top ){ - rc = defragmentPage(pPage); - if( rc ) return rc; - top = get2byteNotZero(&data[hdr+5]); - assert( gap+nByte<=top ); - } - - - /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. The btreeInitPage() call has already - ** validated the freelist. Given that the freelist is valid, there - ** is no way that the allocation can extend off the end of the page. - ** The assert() below verifies the previous sentence. - */ - top -= nByte; - put2byte(&data[hdr+5], top); - assert( top+nByte <= (int)pPage->pBt->usableSize ); - *pIdx = top; - return SQLITE_OK; -} - -/* -** Return a section of the pPage->aData to the freelist. -** The first byte of the new free block is pPage->aDisk[start] -** and the size of the block is "size" bytes. -** -** Most of the effort here is involved in coalesing adjacent -** free blocks into a single big free block. -*/ -static int freeSpace(MemPage *pPage, int start, int size){ - int addr, pbegin, hdr; - int iLast; /* Largest possible freeblock offset */ - unsigned char *data = pPage->aData; - - assert( pPage->pBt!=0 ); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( (start + size) <= (int)pPage->pBt->usableSize ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( size>=0 ); /* Minimum cell size is 4 */ - - if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){ - /* Overwrite deleted information with zeros when the secure_delete - ** option is enabled */ - memset(&data[start], 0, size); - } - - /* Add the space back into the linked list of freeblocks. Note that - ** even though the freeblock list was checked by btreeInitPage(), - ** btreeInitPage() did not detect overlapping cells or - ** freeblocks that overlapped cells. Nor does it detect when the - ** cell content area exceeds the value in the page header. If these - ** situations arise, then subsequent insert operations might corrupt - ** the freelist. So we do need to check for corruption while scanning - ** the freelist. - */ - hdr = pPage->hdrOffset; - addr = hdr + 1; - iLast = pPage->pBt->usableSize - 4; - assert( start<=iLast ); - while( (pbegin = get2byte(&data[addr]))0 ){ - if( pbeginiLast ){ - return SQLITE_CORRUPT_BKPT; - } - assert( pbegin>addr || pbegin==0 ); - put2byte(&data[addr], start); - put2byte(&data[start], pbegin); - put2byte(&data[start+2], size); - pPage->nFree = pPage->nFree + (u16)size; - - /* Coalesce adjacent free blocks */ - addr = hdr + 1; - while( (pbegin = get2byte(&data[addr]))>0 ){ - int pnext, psize, x; - assert( pbegin>addr ); - assert( pbegin <= (int)pPage->pBt->usableSize-4 ); - pnext = get2byte(&data[pbegin]); - psize = get2byte(&data[pbegin+2]); - if( pbegin + psize + 3 >= pnext && pnext>0 ){ - int frag = pnext - (pbegin+psize); - if( (frag<0) || (frag>(int)data[hdr+7]) ){ - return SQLITE_CORRUPT_BKPT; - } - data[hdr+7] -= (u8)frag; - x = get2byte(&data[pnext]); - put2byte(&data[pbegin], x); - x = pnext + get2byte(&data[pnext+2]) - pbegin; - put2byte(&data[pbegin+2], x); - }else{ - addr = pbegin; - } - } - - /* If the cell content area begins with a freeblock, remove it. */ - if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){ - int top; - pbegin = get2byte(&data[hdr+1]); - memcpy(&data[hdr+1], &data[pbegin], 2); - top = get2byte(&data[hdr+5]) + get2byte(&data[pbegin+2]); - put2byte(&data[hdr+5], top); - } - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - return SQLITE_OK; -} - -/* -** Decode the flags byte (the first byte of the header) for a page -** and initialize fields of the MemPage structure accordingly. -** -** Only the following combinations are supported. Anything different -** indicates a corrupt database files: -** -** PTF_ZERODATA -** PTF_ZERODATA | PTF_LEAF -** PTF_LEAFDATA | PTF_INTKEY -** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF -*/ -static int decodeFlags(MemPage *pPage, int flagByte){ - BtShared *pBt; /* A copy of pPage->pBt */ - - assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); - flagByte &= ~PTF_LEAF; - pPage->childPtrSize = 4-4*pPage->leaf; - pBt = pPage->pBt; - if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - pPage->intKey = 1; - pPage->hasData = pPage->leaf; - pPage->maxLocal = pBt->maxLeaf; - pPage->minLocal = pBt->minLeaf; - }else if( flagByte==PTF_ZERODATA ){ - pPage->intKey = 0; - pPage->hasData = 0; - pPage->maxLocal = pBt->maxLocal; - pPage->minLocal = pBt->minLocal; - }else{ - return SQLITE_CORRUPT_BKPT; - } - pPage->max1bytePayload = pBt->max1bytePayload; - return SQLITE_OK; -} - -/* -** Initialize the auxiliary information for a disk block. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. -*/ -static int btreeInitPage(MemPage *pPage){ - - assert( pPage->pBt!=0 ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( pPage->pgno==sqlite4PagerPagenumber(pPage->pDbPage) ); - assert( pPage == sqlite4PagerGetExtra(pPage->pDbPage) ); - assert( pPage->aData == sqlite4PagerGetData(pPage->pDbPage) ); - - if( !pPage->isInit ){ - u16 pc; /* Address of a freeblock within pPage->aData[] */ - u8 hdr; /* Offset to beginning of page header */ - u8 *data; /* Equal to pPage->aData */ - BtShared *pBt; /* The main btree structure */ - int usableSize; /* Amount of usable space on each page */ - u16 cellOffset; /* Offset from start of page to first cell pointer */ - int nFree; /* Number of unused bytes on the page */ - int top; /* First byte of the cell content area */ - int iCellFirst; /* First allowable cell or freeblock offset */ - int iCellLast; /* Last possible cell or freeblock offset */ - - pBt = pPage->pBt; - - hdr = pPage->hdrOffset; - data = pPage->aData; - if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT; - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; - usableSize = pBt->usableSize; - pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; - pPage->aDataEnd = &data[usableSize]; - pPage->aCellIdx = &data[cellOffset]; - top = get2byteNotZero(&data[hdr+5]); - pPage->nCell = get2byte(&data[hdr+3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_BKPT; - } - testcase( pPage->nCell==MX_CELL(pBt) ); - - /* A malformed database page might cause us to read past the end - ** of page when parsing a cell. - ** - ** The following block of code checks early to see if a cell extends - ** past the end of a page boundary and causes SQLITE_CORRUPT to be - ** returned if it does. - */ - iCellFirst = cellOffset + 2*pPage->nCell; - iCellLast = usableSize - 4; -#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) - { - int i; /* Index into the cell pointer array */ - int sz; /* Size of a cell */ - - if( !pPage->leaf ) iCellLast--; - for(i=0; inCell; i++){ - pc = get2byte(&data[cellOffset+i*2]); - testcase( pc==iCellFirst ); - testcase( pc==iCellLast ); - if( pciCellLast ){ - return SQLITE_CORRUPT_BKPT; - } - sz = cellSizePtr(pPage, &data[pc]); - testcase( pc+sz==usableSize ); - if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_BKPT; - } - } - if( !pPage->leaf ) iCellLast++; - } -#endif - - /* Compute the total free space on the page */ - pc = get2byte(&data[hdr+1]); - nFree = data[hdr+7] + top; - while( pc>0 ){ - u16 next, size; - if( pciCellLast ){ - /* Start of free block is off the page */ - return SQLITE_CORRUPT_BKPT; - } - next = get2byte(&data[pc]); - size = get2byte(&data[pc+2]); - if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){ - /* Free blocks must be in ascending order. And the last byte of - ** the free-block must lie on the database page. */ - return SQLITE_CORRUPT_BKPT; - } - nFree = nFree + size; - pc = next; - } - - /* At this point, nFree contains the sum of the offset to the start - ** of the cell-content area plus the number of free bytes within - ** the cell-content area. If this is greater than the usable-size - ** of the page, then the page must be corrupted. This check also - ** serves to verify that the offset to the start of the cell-content - ** area, according to the page header, lies within the page. - */ - if( nFree>usableSize ){ - return SQLITE_CORRUPT_BKPT; - } - pPage->nFree = (u16)(nFree - iCellFirst); - pPage->isInit = 1; - } - return SQLITE_OK; -} - -/* -** Set up a raw page so that it looks like a database page holding -** no entries. -*/ -static void zeroPage(MemPage *pPage, int flags){ - unsigned char *data = pPage->aData; - BtShared *pBt = pPage->pBt; - u8 hdr = pPage->hdrOffset; - u16 first; - - assert( sqlite4PagerPagenumber(pPage->pDbPage)==pPage->pgno ); - assert( sqlite4PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlite4PagerGetData(pPage->pDbPage) == data ); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - assert( sqlite4_mutex_held(pBt->mutex) ); - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - memset(&data[hdr], 0, pBt->usableSize - hdr); - } - data[hdr] = (char)flags; - first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); - memset(&data[hdr+1], 0, 4); - data[hdr+7] = 0; - put2byte(&data[hdr+5], pBt->usableSize); - pPage->nFree = (u16)(pBt->usableSize - first); - decodeFlags(pPage, flags); - pPage->hdrOffset = hdr; - pPage->cellOffset = first; - pPage->aDataEnd = &data[pBt->usableSize]; - pPage->aCellIdx = &data[first]; - pPage->nOverflow = 0; - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nCell = 0; - pPage->isInit = 1; -} - - -/* -** Convert a DbPage obtained from the pager into a MemPage used by -** the btree layer. -*/ -static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){ - MemPage *pPage = (MemPage*)sqlite4PagerGetExtra(pDbPage); - pPage->aData = sqlite4PagerGetData(pDbPage); - pPage->pDbPage = pDbPage; - pPage->pBt = pBt; - pPage->pgno = pgno; - pPage->hdrOffset = pPage->pgno==1 ? 100 : 0; - return pPage; -} - -/* -** Get a page from the pager. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -** -** If the noContent flag is set, it means that we do not care about -** the content of the page at this time. So do not go to the disk -** to fetch the content. Just fill in the content with zeros for now. -** If in the future we call sqlite4PagerWrite() on this page, that -** means we have started to be concerned about content and the disk -** read should occur at that point. -*/ -static int btreeGetPage( - BtShared *pBt, /* The btree */ - Pgno pgno, /* Number of the page to fetch */ - MemPage **ppPage, /* Return the page in this parameter */ - int noContent /* Do not load page content if true */ -){ - int rc; - DbPage *pDbPage; - - assert( sqlite4_mutex_held(pBt->mutex) ); - rc = sqlite4PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent); - if( rc ) return rc; - *ppPage = btreePageFromDbPage(pDbPage, pgno, pBt); - return SQLITE_OK; -} - -/* -** Retrieve a page from the pager cache. If the requested page is not -** already in the pager cache return NULL. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -*/ -static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){ - DbPage *pDbPage; - assert( sqlite4_mutex_held(pBt->mutex) ); - pDbPage = sqlite4PagerLookup(pBt->pPager, pgno); - if( pDbPage ){ - return btreePageFromDbPage(pDbPage, pgno, pBt); - } - return 0; -} - -/* -** Return the size of the database file in pages. If there is any kind of -** error, return ((unsigned int)-1). -*/ -static Pgno btreePagecount(BtShared *pBt){ - return pBt->nPage; -} -u32 sqlite4BtreeLastPage(Btree *p){ - assert( sqlite4BtreeHoldsMutex(p) ); - assert( ((p->pBt->nPage)&0x8000000)==0 ); - return (int)btreePagecount(p->pBt); -} - -/* -** Get a page from the pager and initialize it. This routine is just a -** convenience wrapper around separate calls to btreeGetPage() and -** btreeInitPage(). -** -** If an error occurs, then the value *ppPage is set to is undefined. It -** may remain unchanged, or it may be set to an invalid value. -*/ -static int getAndInitPage( - BtShared *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage /* Write the page pointer here */ -){ - int rc; - assert( sqlite4_mutex_held(pBt->mutex) ); - - if( pgno>btreePagecount(pBt) ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = btreeGetPage(pBt, pgno, ppPage, 0); - if( rc==SQLITE_OK ){ - rc = btreeInitPage(*ppPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - } - } - - testcase( pgno==0 ); - assert( pgno!=0 || rc==SQLITE_CORRUPT ); - return rc; -} - -/* -** Release a MemPage. This should be called once for each prior -** call to btreeGetPage. -*/ -static void releasePage(MemPage *pPage){ - if( pPage ){ - assert( pPage->aData ); - assert( pPage->pBt ); - assert( sqlite4PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlite4PagerGetData(pPage->pDbPage)==pPage->aData ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - sqlite4PagerUnref(pPage->pDbPage); - } -} - -/* -** During a rollback, when the pager reloads information into the cache -** so that the cache is restored to its original state at the start of -** the transaction, for each page restored this routine is called. -** -** This routine needs to reset the extra data section at the end of the -** page to agree with the restored data. -*/ -static void pageReinit(DbPage *pData){ - MemPage *pPage; - pPage = (MemPage *)sqlite4PagerGetExtra(pData); - assert( sqlite4PagerPageRefcount(pData)>0 ); - if( pPage->isInit ){ - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - pPage->isInit = 0; - if( sqlite4PagerPageRefcount(pData)>1 ){ - /* pPage might not be a btree page; it might be an overflow page - ** or ptrmap page or a free page. In those cases, the following - ** call to btreeInitPage() will likely return SQLITE_CORRUPT. - ** But no harm is done by this. And it is very important that - ** btreeInitPage() be called on every btree page so we make - ** the call for every page that comes in for re-initing. */ - btreeInitPage(pPage); - } - } -} - -/* -** Invoke the busy handler for a btree. -*/ -static int btreeInvokeBusyHandler(void *pArg){ - BtShared *pBt = (BtShared*)pArg; - assert( pBt->db ); - assert( sqlite4_mutex_held(pBt->db->mutex) ); - return sqlite4InvokeBusyHandler(&pBt->db->busyHandler); -} - -/* -** Open a database file. -** -** zFilename is the name of the database file. If zFilename is NULL -** 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 sqlite4BtreeClose() 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 like -** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY. -** -** 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 -** objects in the same database connection since doing so will lead -** to problems with locking. -*/ -int sqlite4BtreeOpen( - sqlite4_vfs *pVfs, /* VFS to use for this b-tree */ - const char *zFilename, /* Name of the file containing the BTree database */ - sqlite4 *db, /* Associated database handle */ - Btree **ppBtree, /* Pointer to new Btree object written here */ - int flags, /* Options */ - int vfsFlags /* Flags passed through to sqlite4_vfs.xOpen() */ -){ - BtShared *pBt = 0; /* Shared part of btree structure */ - Btree *p; /* Handle to return */ - sqlite4_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ - int rc = SQLITE_OK; /* Result code from this function */ - 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. - */ -#ifdef SQLITE_OMIT_MEMORYDB - const int isMemdb = 0; -#else - const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) - || (isTempDb && sqlite4TempInMemory(db)); -#endif - - assert( db!=0 ); - assert( pVfs!=0 ); - assert( sqlite4_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( isMemdb ){ - flags |= BTREE_MEMORY; - } - if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ - vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; - } - p = sqlite4MallocZero(sizeof(Btree)); - if( !p ){ - return SQLITE_NOMEM; - } - p->inTrans = TRANS_NONE; - p->db = db; -#ifndef SQLITE_OMIT_SHARED_CACHE - p->lock.pBtree = p; - p->lock.iTable = 1; -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* - ** If this Btree is a candidate for shared cache, try to find an - ** existing BtShared object that we can share with - */ - if( isMemdb==0 && isTempDb==0 ){ - if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ - int nFullPathname = pVfs->mxPathname+1; - char *zFullPathname = sqlite4Malloc(nFullPathname); - MUTEX_LOGIC( sqlite4_mutex *mutexShared; ) - p->sharable = 1; - if( !zFullPathname ){ - sqlite4_free(p); - return SQLITE_NOMEM; - } - rc = sqlite4OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); - if( rc ){ - sqlite4_free(zFullPathname); - sqlite4_free(p); - return rc; - } -#if SQLITE_THREADSAFE - mutexOpen = sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); - sqlite4_mutex_enter(mutexOpen); - mutexShared = sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); - sqlite4_mutex_enter(mutexShared); -#endif - for(pBt=GLOBAL(BtShared*,sqlite4SharedCacheList); pBt; pBt=pBt->pNext){ - assert( pBt->nRef>0 ); - if( 0==strcmp(zFullPathname, sqlite4PagerFilename(pBt->pPager)) - && sqlite4PagerVfs(pBt->pPager)==pVfs ){ - int iDb; - for(iDb=db->nDb-1; iDb>=0; iDb--){ - Btree *pExisting = db->aDb[iDb].pBt; - if( pExisting && pExisting->pBt==pBt ){ - sqlite4_mutex_leave(mutexShared); - sqlite4_mutex_leave(mutexOpen); - sqlite4_free(zFullPathname); - sqlite4_free(p); - return SQLITE_CONSTRAINT; - } - } - p->pBt = pBt; - pBt->nRef++; - break; - } - } - sqlite4_mutex_leave(mutexShared); - sqlite4_free(zFullPathname); - } -#ifdef SQLITE_DEBUG - else{ - /* In debug mode, we mark all persistent databases as sharable - ** even when they are not. This exercises the locking code and - ** gives more opportunity for asserts(sqlite4_mutex_held()) - ** statements to find locking problems. - */ - p->sharable = 1; - } -#endif - } -#endif - if( pBt==0 ){ - /* - ** The following asserts make sure that structures used by the btree are - ** the right size. This is to guard against size changes that result - ** when compiling on a different architecture. - */ - assert( sizeof(i64)==8 || sizeof(i64)==4 ); - assert( sizeof(u64)==8 || sizeof(u64)==4 ); - assert( sizeof(u32)==4 ); - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); - - pBt = sqlite4MallocZero( sizeof(*pBt) ); - if( pBt==0 ){ - rc = SQLITE_NOMEM; - goto btree_open_out; - } - rc = sqlite4PagerOpen(pVfs, &pBt->pPager, zFilename, - EXTRA_SIZE, flags, vfsFlags, pageReinit); - if( rc==SQLITE_OK ){ - rc = sqlite4PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); - } - if( rc!=SQLITE_OK ){ - goto btree_open_out; - } - pBt->openFlags = (u8)flags; - pBt->db = db; - sqlite4PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); - p->pBt = pBt; - - pBt->pCursor = 0; - pBt->pPage1 = 0; - if( sqlite4PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; -#ifdef SQLITE_SECURE_DELETE - pBt->btsFlags |= BTS_SECURE_DELETE; -#endif - pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); - if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE - || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ - pBt->pageSize = 0; -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the magic name ":memory:" will create an in-memory database, then - ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if - ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if - ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a - ** regular file-name. In this case the auto-vacuum applies as per normal. - */ - if( zFilename && !isMemdb ){ - pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); - pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); - } -#endif - nReserve = 0; - }else{ - nReserve = zDbHeader[20]; - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); -#endif - } - rc = sqlite4PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - if( rc ) goto btree_open_out; - pBt->usableSize = pBt->pageSize - nReserve; - assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* Add the new BtShared object to the linked list sharable BtShareds. - */ - if( p->sharable ){ - MUTEX_LOGIC( sqlite4_mutex *mutexShared; ) - pBt->nRef = 1; - MUTEX_LOGIC( mutexShared = sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);) - if( SQLITE_THREADSAFE && sqlite4GlobalConfig.bCoreMutex ){ - pBt->mutex = sqlite4MutexAlloc(SQLITE_MUTEX_FAST); - if( pBt->mutex==0 ){ - rc = SQLITE_NOMEM; - db->mallocFailed = 0; - goto btree_open_out; - } - } - sqlite4_mutex_enter(mutexShared); - pBt->pNext = GLOBAL(BtShared*,sqlite4SharedCacheList); - GLOBAL(BtShared*,sqlite4SharedCacheList) = pBt; - sqlite4_mutex_leave(mutexShared); - } -#endif - } - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* If the new Btree uses a sharable pBtShared, then link the new - ** Btree into the list of all sharable Btrees for the same connection. - ** The list is kept in ascending order by pBt address. - */ - if( p->sharable ){ - int i; - Btree *pSib; - for(i=0; inDb; i++){ - if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){ - while( pSib->pPrev ){ pSib = pSib->pPrev; } - if( p->pBtpBt ){ - p->pNext = pSib; - p->pPrev = 0; - pSib->pPrev = p; - }else{ - while( pSib->pNext && pSib->pNext->pBtpBt ){ - pSib = pSib->pNext; - } - p->pNext = pSib->pNext; - p->pPrev = pSib; - if( p->pNext ){ - p->pNext->pPrev = p; - } - pSib->pNext = p; - } - break; - } - } - } -#endif - *ppBtree = p; - -btree_open_out: - if( rc!=SQLITE_OK ){ - if( pBt && pBt->pPager ){ - sqlite4PagerClose(pBt->pPager); - } - sqlite4_free(pBt); - sqlite4_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( sqlite4BtreeSchema(p, 0, 0)==0 ){ - sqlite4PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); - } - } - if( mutexOpen ){ - assert( sqlite4_mutex_held(mutexOpen) ); - sqlite4_mutex_leave(mutexOpen); - } - return rc; -} - -/* -** Decrement the BtShared.nRef counter. When it reaches zero, -** remove the BtShared structure from the sharing list. Return -** true if the BtShared.nRef counter reaches zero and return -** false if it is still positive. -*/ -static int removeFromSharingList(BtShared *pBt){ -#ifndef SQLITE_OMIT_SHARED_CACHE - MUTEX_LOGIC( sqlite4_mutex *pMaster; ) - BtShared *pList; - int removed = 0; - - assert( sqlite4_mutex_notheld(pBt->mutex) ); - MUTEX_LOGIC( pMaster = sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) - sqlite4_mutex_enter(pMaster); - pBt->nRef--; - if( pBt->nRef<=0 ){ - if( GLOBAL(BtShared*,sqlite4SharedCacheList)==pBt ){ - GLOBAL(BtShared*,sqlite4SharedCacheList) = pBt->pNext; - }else{ - pList = GLOBAL(BtShared*,sqlite4SharedCacheList); - while( ALWAYS(pList) && pList->pNext!=pBt ){ - pList=pList->pNext; - } - if( ALWAYS(pList) ){ - pList->pNext = pBt->pNext; - } - } - if( SQLITE_THREADSAFE ){ - sqlite4_mutex_free(pBt->mutex); - } - removed = 1; - } - sqlite4_mutex_leave(pMaster); - return removed; -#else - return 1; -#endif -} - -/* -** Make sure pBt->pTmpSpace points to an allocation of -** MX_CELL_SIZE(pBt) bytes. -*/ -static void allocateTempSpace(BtShared *pBt){ - if( !pBt->pTmpSpace ){ - pBt->pTmpSpace = sqlite4PageMalloc( pBt->pageSize ); - } -} - -/* -** Free the pBt->pTmpSpace allocation -*/ -static void freeTempSpace(BtShared *pBt){ - sqlite4PageFree( pBt->pTmpSpace); - pBt->pTmpSpace = 0; -} - -/* -** Close an open database and invalidate all cursors. -*/ -int sqlite4BtreeClose(Btree *p){ - BtShared *pBt = p->pBt; - BtCursor *pCur; - - /* Close all cursors opened via this handle. */ - assert( sqlite4_mutex_held(p->db->mutex) ); - sqlite4BtreeEnter(p); - pCur = pBt->pCursor; - while( pCur ){ - BtCursor *pTmp = pCur; - pCur = pCur->pNext; - if( pTmp->pBtree==p ){ - sqlite4BtreeCloseCursor(pTmp); - } - } - - /* Rollback any active transaction and free the handle structure. - ** The call to sqlite4BtreeRollback() drops any table-locks held by - ** this handle. - */ - sqlite4BtreeRollback(p); - sqlite4BtreeLeave(p); - - /* If there are still other outstanding references to the shared-btree - ** structure, return now. The remainder of this procedure cleans - ** up the shared-btree. - */ - assert( p->wantToLock==0 && p->locked==0 ); - if( !p->sharable || removeFromSharingList(pBt) ){ - /* The pBt is no longer on the sharing list, so we can access - ** it without having to hold the mutex. - ** - ** Clean out and delete the BtShared object. - */ - assert( !pBt->pCursor ); - sqlite4PagerClose(pBt->pPager); - if( pBt->xFreeSchema && pBt->pSchema ){ - pBt->xFreeSchema(pBt->pSchema); - } - sqlite4DbFree(0, pBt->pSchema); - freeTempSpace(pBt); - sqlite4_free(pBt); - } - -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( p->wantToLock==0 ); - assert( p->locked==0 ); - if( p->pPrev ) p->pPrev->pNext = p->pNext; - if( p->pNext ) p->pNext->pPrev = p->pPrev; -#endif - - sqlite4_free(p); - return SQLITE_OK; -} - -/* -** Change the limit on the number of pages allowed in the cache. -** -** The maximum number of cache pages is set to the absolute -** value of mxPage. If mxPage is negative, the pager will -** operate asynchronously - it will not stop to do fsync()s -** to insure data is written to the disk surface before -** continuing. Transactions still work if synchronous is off, -** and the database cannot be corrupted if this program -** crashes. But if the operating system crashes or there is -** an abrupt power failure when synchronous is off, the database -** could be left in an inconsistent and unrecoverable state. -** Synchronous is on by default so database corruption is not -** normally a worry. -*/ -int sqlite4BtreeSetCacheSize(Btree *p, int mxPage){ - BtShared *pBt = p->pBt; - assert( sqlite4_mutex_held(p->db->mutex) ); - sqlite4BtreeEnter(p); - sqlite4PagerSetCachesize(pBt->pPager, mxPage); - sqlite4BtreeLeave(p); - return SQLITE_OK; -} - -/* -** Change the way data is synced to disk in order to increase or decrease -** how well the database resists damage due to OS crashes and power -** failures. Level 1 is the same as asynchronous (no syncs() occur and -** there is a high probability of damage) Level 2 is the default. There -** is a very low but non-zero probability of damage. Level 3 reduces the -** probability of damage to near zero but with a write performance reduction. -*/ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -int sqlite4BtreeSetSafetyLevel( - 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( sqlite4_mutex_held(p->db->mutex) ); - assert( level>=1 && level<=3 ); - sqlite4BtreeEnter(p); - sqlite4PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync); - sqlite4BtreeLeave(p); - return SQLITE_OK; -} -#endif - -/* -** Return TRUE if the given btree is set to safety level 1. In other -** words, return TRUE if no sync() occurs on the disk files. -*/ -int sqlite4BtreeSyncDisabled(Btree *p){ - BtShared *pBt = p->pBt; - int rc; - assert( sqlite4_mutex_held(p->db->mutex) ); - sqlite4BtreeEnter(p); - assert( pBt && pBt->pPager ); - rc = sqlite4PagerNosync(pBt->pPager); - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Change the default pages size and the number of reserved bytes per page. -** Or, if the page size has already been fixed, return SQLITE_READONLY -** without changing anything. -** -** The page size must be a power of 2 between 512 and 65536. If the page -** size supplied does not meet this constraint then the page size is not -** changed. -** -** Page sizes are constrained to be a power of two so that the region -** of the database file used for locking (beginning at PENDING_BYTE, -** the first byte past the 1GB boundary, 0x40000000) needs to occur -** at the beginning of a page. -** -** If parameter nReserve is less than zero, then the number of reserved -** bytes per page is left unchanged. -** -** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size -** and autovacuum mode can no longer be changed. -*/ -int sqlite4BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ - int rc = SQLITE_OK; - BtShared *pBt = p->pBt; - assert( nReserve>=-1 && nReserve<=255 ); - sqlite4BtreeEnter(p); - if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ - sqlite4BtreeLeave(p); - return SQLITE_READONLY; - } - if( nReserve<0 ){ - nReserve = pBt->pageSize - pBt->usableSize; - } - assert( nReserve>=0 && nReserve<=255 ); - if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && - ((pageSize-1)&pageSize)==0 ){ - assert( (pageSize & 7)==0 ); - assert( !pBt->pPage1 && !pBt->pCursor ); - pBt->pageSize = (u32)pageSize; - freeTempSpace(pBt); - } - rc = sqlite4PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - pBt->usableSize = pBt->pageSize - (u16)nReserve; - if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED; - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Return the currently defined page size -*/ -int sqlite4BtreeGetPageSize(Btree *p){ - return p->pBt->pageSize; -} - -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) -/* -** Return the number of bytes of space at the end of every page that -** are intentually left unused. This is the "reserved" space that is -** sometimes used by extensions. -*/ -int sqlite4BtreeGetReserve(Btree *p){ - int n; - sqlite4BtreeEnter(p); - n = p->pBt->pageSize - p->pBt->usableSize; - sqlite4BtreeLeave(p); - return n; -} - -/* -** Set the maximum page count for a database if mxPage is positive. -** No changes are made if mxPage is 0 or negative. -** Regardless of the value of mxPage, return the maximum page count. -*/ -int sqlite4BtreeMaxPageCount(Btree *p, int mxPage){ - int n; - sqlite4BtreeEnter(p); - n = sqlite4PagerMaxPageCount(p->pBt->pPager, mxPage); - sqlite4BtreeLeave(p); - return n; -} - -/* -** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1, -** then make no changes. Always return the value of the BTS_SECURE_DELETE -** setting after the change. -*/ -int sqlite4BtreeSecureDelete(Btree *p, int newFlag){ - int b; - if( p==0 ) return 0; - sqlite4BtreeEnter(p); - if( newFlag>=0 ){ - p->pBt->btsFlags &= ~BTS_SECURE_DELETE; - if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE; - } - b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0; - sqlite4BtreeLeave(p); - return b; -} -#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ - -/* -** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' -** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it -** is disabled. The default value for the auto-vacuum property is -** determined by the SQLITE_DEFAULT_AUTOVACUUM macro. -*/ -int sqlite4BtreeSetAutoVacuum(Btree *p, int autoVacuum){ -#ifdef SQLITE_OMIT_AUTOVACUUM - return SQLITE_READONLY; -#else - BtShared *pBt = p->pBt; - int rc = SQLITE_OK; - u8 av = (u8)autoVacuum; - - sqlite4BtreeEnter(p); - if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){ - rc = SQLITE_READONLY; - }else{ - pBt->autoVacuum = av ?1:0; - pBt->incrVacuum = av==2 ?1:0; - } - sqlite4BtreeLeave(p); - return rc; -#endif -} - -/* -** Return the value of the 'auto-vacuum' property. If auto-vacuum is -** enabled 1 is returned. Otherwise 0. -*/ -int sqlite4BtreeGetAutoVacuum(Btree *p){ -#ifdef SQLITE_OMIT_AUTOVACUUM - return BTREE_AUTOVACUUM_NONE; -#else - int rc; - sqlite4BtreeEnter(p); - rc = ( - (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE: - (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL: - BTREE_AUTOVACUUM_INCR - ); - sqlite4BtreeLeave(p); - return rc; -#endif -} - - -/* -** Get a reference to pPage1 of the database file. This will -** also acquire a readlock on that file. -** -** SQLITE_OK is returned on success. If the file is not a -** well-formed database file, then SQLITE_CORRUPT is returned. -** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM -** is returned if we run out of memory. -*/ -static int lockBtree(BtShared *pBt){ - int rc; /* Result code from subfunctions */ - MemPage *pPage1; /* Page 1 of the database file */ - int nPage; /* Number of pages in the database */ - int nPageFile = 0; /* Number of pages in the database file */ - int nPageHeader; /* Number of pages in the database according to hdr */ - - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( pBt->pPage1==0 ); - rc = sqlite4PagerSharedLock(pBt->pPager); - if( rc!=SQLITE_OK ) return rc; - rc = btreeGetPage(pBt, 1, &pPage1, 0); - if( rc!=SQLITE_OK ) return rc; - - /* Do some checking to help insure the file we opened really is - ** a valid database file. - */ - nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); - sqlite4PagerPagecount(pBt->pPager, &nPageFile); - if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ - nPage = nPageFile; - } - if( nPage>0 ){ - u32 pageSize; - u32 usableSize; - u8 *page1 = pPage1->aData; - rc = SQLITE_NOTADB; - if( memcmp(page1, zMagicHeader, 16)!=0 ){ - goto page1_init_failed; - } - -#ifdef SQLITE_OMIT_WAL - if( page1[18]>1 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>1 ){ - goto page1_init_failed; - } -#else - if( page1[18]>2 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>2 ){ - goto page1_init_failed; - } - - /* If the write version is set to 2, this database should be accessed - ** in WAL mode. If the log is not already open, open it now. Then - ** return SQLITE_OK and return without populating BtShared.pPage1. - ** The caller detects this and calls this function again. This is - ** required as the version of page 1 currently in the page1 buffer - ** may not be the latest version - there may be a newer one in the log - ** file. - */ - if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ - int isOpen = 0; - rc = sqlite4PagerOpenWal(pBt->pPager, &isOpen); - if( rc!=SQLITE_OK ){ - goto page1_init_failed; - }else if( isOpen==0 ){ - releasePage(pPage1); - return SQLITE_OK; - } - rc = SQLITE_NOTADB; - } -#endif - - /* The maximum embedded fraction must be exactly 25%. And the minimum - ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. - ** The original design allowed these amounts to vary, but as of - ** version 3.6.0, we require them to be fixed. - */ - if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ - goto page1_init_failed; - } - pageSize = (page1[16]<<8) | (page1[17]<<16); - if( ((pageSize-1)&pageSize)!=0 - || pageSize>SQLITE_MAX_PAGE_SIZE - || pageSize<=256 - ){ - goto page1_init_failed; - } - assert( (pageSize & 7)==0 ); - usableSize = pageSize - page1[20]; - if( (u32)pageSize!=pBt->pageSize ){ - /* After reading the first page of the database assuming a page size - ** of BtShared.pageSize, we have discovered that the page-size is - ** actually pageSize. Unlock the database, leave pBt->pPage1 at - ** zero and return SQLITE_OK. The caller will call this function - ** again with the correct page-size. - */ - releasePage(pPage1); - pBt->usableSize = usableSize; - pBt->pageSize = pageSize; - freeTempSpace(pBt); - rc = sqlite4PagerSetPagesize(pBt->pPager, &pBt->pageSize, - pageSize-usableSize); - return rc; - } - if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){ - rc = SQLITE_CORRUPT_BKPT; - goto page1_init_failed; - } - if( usableSize<480 ){ - goto page1_init_failed; - } - pBt->pageSize = pageSize; - pBt->usableSize = usableSize; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0); -#endif - } - - /* maxLocal is the maximum amount of payload to store locally for - ** a cell. Make sure it is small enough so that at least minFanout - ** cells can will fit on one page. We assume a 10-byte page header. - ** Besides the payload, the cell must store: - ** 2-byte pointer to the cell - ** 4-byte child pointer - ** 9-byte nKey value - ** 4-byte nData value - ** 4-byte overflow page pointer - ** So a cell consists of a 2-byte pointer, a header which is as much as - ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow - ** page pointer. - */ - pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); - pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); - pBt->maxLeaf = (u16)(pBt->usableSize - 35); - pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); - if( pBt->maxLocal>127 ){ - pBt->max1bytePayload = 127; - }else{ - pBt->max1bytePayload = (u8)pBt->maxLocal; - } - assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); - pBt->pPage1 = pPage1; - pBt->nPage = nPage; - return SQLITE_OK; - -page1_init_failed: - releasePage(pPage1); - pBt->pPage1 = 0; - return rc; -} - -/* -** If there are no outstanding cursors and we are not in the middle -** of a transaction but there is a read lock on the database, then -** this routine unrefs the first page of the database file which -** has the effect of releasing the read lock. -** -** If there is a transaction in progress, this routine is a no-op. -*/ -static void unlockBtreeIfUnused(BtShared *pBt){ - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); - if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ - assert( pBt->pPage1->aData ); - assert( sqlite4PagerRefcount(pBt->pPager)==1 ); - assert( pBt->pPage1->aData ); - releasePage(pBt->pPage1); - pBt->pPage1 = 0; - } -} - -/* -** If pBt points to an empty file then convert that empty file -** into a new empty database by initializing the first page of -** the database. -*/ -static int newDatabase(BtShared *pBt){ - MemPage *pP1; - unsigned char *data; - int rc; - - assert( sqlite4_mutex_held(pBt->mutex) ); - if( pBt->nPage>0 ){ - return SQLITE_OK; - } - pP1 = pBt->pPage1; - assert( pP1!=0 ); - data = pP1->aData; - rc = sqlite4PagerWrite(pP1->pDbPage); - if( rc ) return rc; - memcpy(data, zMagicHeader, sizeof(zMagicHeader)); - assert( sizeof(zMagicHeader)==16 ); - data[16] = (u8)((pBt->pageSize>>8)&0xff); - data[17] = (u8)((pBt->pageSize>>16)&0xff); - data[18] = 1; - data[19] = 1; - assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); - data[20] = (u8)(pBt->pageSize - pBt->usableSize); - data[21] = 64; - data[22] = 32; - data[23] = 32; - memset(&data[24], 0, 100-24); - zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); - assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); - put4byte(&data[36 + 4*4], pBt->autoVacuum); - put4byte(&data[36 + 7*4], pBt->incrVacuum); -#endif - pBt->nPage = 1; - data[31] = 1; - return SQLITE_OK; -} - -/* -** Attempt to start a new transaction. A write-transaction -** is started if the second argument is nonzero, otherwise a read- -** transaction. If the second argument is 2 or more and exclusive -** transaction is started, meaning that no other process is allowed -** to access the database. A preexisting transaction may not be -** upgraded to exclusive by calling this routine a second time - the -** exclusivity flag only works for a new transaction. -** -** A write-transaction must be started before attempting any -** changes to the database. None of the following routines -** will work unless a transaction is started first: -** -** sqlite4BtreeCreateTable() -** sqlite4BtreeCreateIndex() -** sqlite4BtreeClearTable() -** sqlite4BtreeDropTable() -** sqlite4BtreeInsert() -** sqlite4BtreeDelete() -** sqlite4BtreeUpdateMeta() -** -** If an initial attempt to acquire the lock fails because of lock contention -** and the database was previously unlocked, then invoke the busy handler -** if there is one. But if there was previously a read-lock, do not -** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is -** returned when there is already a read-lock in order to avoid a deadlock. -** -** Suppose there are two processes A and B. A has a read lock and B has -** a reserved lock. B tries to promote to exclusive but is blocked because -** of A's read lock. A tries to promote to reserved but is blocked by B. -** One or the other of the two processes must give way or there can be -** no progress. By returning SQLITE_BUSY and not invoking the busy callback -** when A already has a read lock, we encourage A to give up and let B -** proceed. -*/ -int sqlite4BtreeBeginTrans(Btree *p, int wrflag){ - sqlite4 *pBlock = 0; - BtShared *pBt = p->pBt; - int rc = SQLITE_OK; - - sqlite4BtreeEnter(p); - btreeIntegrity(p); - - /* If the btree is already in a write-transaction, or it - ** is already in a read-transaction and a read-transaction - ** is requested, this is a no-op. - */ - if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ - goto trans_begun; - } - - /* Write transactions are not possible on a read-only database */ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ - rc = SQLITE_READONLY; - goto trans_begun; - } - -#ifndef SQLITE_OMIT_SHARED_CACHE - /* If another database handle has already opened a write transaction - ** on this shared-btree structure and a second write transaction is - ** requested, return SQLITE_LOCKED. - */ - if( (wrflag && pBt->inTransaction==TRANS_WRITE) - || (pBt->btsFlags & BTS_PENDING)!=0 - ){ - pBlock = pBt->pWriter->db; - }else if( wrflag>1 ){ - BtLock *pIter; - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->pBtree!=p ){ - pBlock = pIter->pBtree->db; - break; - } - } - } - if( pBlock ){ - sqlite4ConnectionBlocked(p->db, pBlock); - rc = SQLITE_LOCKED_SHAREDCACHE; - goto trans_begun; - } -#endif - - /* Any read-only or read-write transaction implies a read-lock on - ** page 1. So if some other shared-cache client already has a write-lock - ** on page 1, the transaction cannot be opened. */ - rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); - if( SQLITE_OK!=rc ) goto trans_begun; - - pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; - if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; - do { - /* Call lockBtree() until either pBt->pPage1 is populated or - ** lockBtree() returns something other than SQLITE_OK. lockBtree() - ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after - ** reading page 1 it discovers that the page-size of the database - ** file is not pBt->pageSize. In this case lockBtree() will update - ** pBt->pageSize to the page-size of the file on disk. - */ - while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); - - if( rc==SQLITE_OK && wrflag ){ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ - rc = SQLITE_READONLY; - }else{ - rc = sqlite4PagerBegin(pBt->pPager,wrflag>1,sqlite4TempInMemory(p->db)); - if( rc==SQLITE_OK ){ - rc = newDatabase(pBt); - } - } - } - - if( rc!=SQLITE_OK ){ - unlockBtreeIfUnused(pBt); - } - }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && - btreeInvokeBusyHandler(pBt) ); - - if( rc==SQLITE_OK ){ - if( p->inTrans==TRANS_NONE ){ - pBt->nTransaction++; -#ifndef SQLITE_OMIT_SHARED_CACHE - if( p->sharable ){ - assert( p->lock.pBtree==p && p->lock.iTable==1 ); - p->lock.eLock = READ_LOCK; - p->lock.pNext = pBt->pLock; - pBt->pLock = &p->lock; - } -#endif - } - p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); - if( p->inTrans>pBt->inTransaction ){ - pBt->inTransaction = p->inTrans; - } - if( wrflag ){ - MemPage *pPage1 = pBt->pPage1; -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( !pBt->pWriter ); - pBt->pWriter = p; - pBt->btsFlags &= ~BTS_EXCLUSIVE; - if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE; -#endif - - /* If the db-size header field is incorrect (as it may be if an old - ** client has been writing the database file), update it now. Doing - ** this sooner rather than later means the database size can safely - ** re-read the database size from page 1 if a savepoint or transaction - ** rollback occurs within the transaction. - */ - if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){ - rc = sqlite4PagerWrite(pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pPage1->aData[28], pBt->nPage); - } - } - } - } - - -trans_begun: - if( rc==SQLITE_OK && wrflag ){ - /* This call makes sure that the pager has the correct number of - ** open savepoints. If the second parameter is greater than 0 and - ** the sub-journal is not already open, then it will be opened here. - */ - rc = sqlite4PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); - } - - btreeIntegrity(p); - sqlite4BtreeLeave(p); - return rc; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM - -/* -** Set the pointer-map entries for all children of page pPage. Also, if -** pPage contains cells that point to overflow pages, set the pointer -** map entries for the overflow pages as well. -*/ -static int setChildPtrmaps(MemPage *pPage){ - int i; /* Counter variable */ - int nCell; /* Number of cells in page pPage */ - int rc; /* Return code */ - BtShared *pBt = pPage->pBt; - u8 isInitOrig = pPage->isInit; - Pgno pgno = pPage->pgno; - - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - rc = btreeInitPage(pPage); - if( rc!=SQLITE_OK ){ - goto set_child_ptrmaps_out; - } - nCell = pPage->nCell; - - for(i=0; ileaf ){ - Pgno childPgno = get4byte(pCell); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); - } - } - - if( !pPage->leaf ){ - Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); - } - -set_child_ptrmaps_out: - pPage->isInit = isInitOrig; - return rc; -} - -/* -** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so -** that it points to iTo. Parameter eType describes the type of pointer to -** be modified, as follows: -** -** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child -** page of pPage. -** -** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow -** page pointed to by one of the cells on pPage. -** -** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next -** overflow page in the list. -*/ -static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - if( eType==PTRMAP_OVERFLOW2 ){ - /* The pointer is always the first 4 bytes of the page in this case. */ - if( get4byte(pPage->aData)!=iFrom ){ - return SQLITE_CORRUPT_BKPT; - } - put4byte(pPage->aData, iTo); - }else{ - u8 isInitOrig = pPage->isInit; - int i; - int nCell; - - btreeInitPage(pPage); - nCell = pPage->nCell; - - for(i=0; iaData+pPage->maskPage - && iFrom==get4byte(&pCell[info.iOverflow]) - ){ - put4byte(&pCell[info.iOverflow], iTo); - break; - } - }else{ - if( get4byte(pCell)==iFrom ){ - put4byte(pCell, iTo); - break; - } - } - } - - if( i==nCell ){ - if( eType!=PTRMAP_BTREE || - get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ - return SQLITE_CORRUPT_BKPT; - } - put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); - } - - pPage->isInit = isInitOrig; - } - return SQLITE_OK; -} - - -/* -** Move the open database page pDbPage to location iFreePage in the -** database. The pDbPage reference remains valid. -** -** The isCommit flag indicates that there is no need to remember that -** the journal needs to be sync()ed before database page pDbPage->pgno -** can be written to. The caller has already promised not to write to that -** page. -*/ -static int relocatePage( - BtShared *pBt, /* Btree */ - MemPage *pDbPage, /* Open page to move */ - u8 eType, /* Pointer map 'type' entry for pDbPage */ - Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ - Pgno iFreePage, /* The location to move pDbPage to */ - int isCommit /* isCommit flag passed to sqlite4PagerMovepage */ -){ - MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ - Pgno iDbPage = pDbPage->pgno; - Pager *pPager = pBt->pPager; - int rc; - - assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || - eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( pDbPage->pBt==pBt ); - - /* Move page iDbPage from its current location to page number iFreePage */ - TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", - iDbPage, iFreePage, iPtrPage, eType)); - rc = sqlite4PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); - if( rc!=SQLITE_OK ){ - return rc; - } - pDbPage->pgno = iFreePage; - - /* If pDbPage was a btree-page, then it may have child pages and/or cells - ** that point to overflow pages. The pointer map entries for all these - ** pages need to be changed. - ** - ** If pDbPage is an overflow page, then the first 4 bytes may store a - ** pointer to a subsequent overflow page. If this is the case, then - ** the pointer map needs to be updated for the subsequent overflow page. - */ - if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ - rc = setChildPtrmaps(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - Pgno nextOvfl = get4byte(pDbPage->aData); - if( nextOvfl!=0 ){ - ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); - if( rc!=SQLITE_OK ){ - return rc; - } - } - } - - /* Fix the database pointer on page iPtrPage that pointed at iDbPage so - ** that it points at iFreePage. Also fix the pointer map entry for - ** iPtrPage. - */ - if( eType!=PTRMAP_ROOTPAGE ){ - rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite4PagerWrite(pPtrPage->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pPtrPage); - return rc; - } - rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); - releasePage(pPtrPage); - if( rc==SQLITE_OK ){ - ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); - } - } - return rc; -} - -/* Forward declaration required by incrVacuumStep(). */ -static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); - -/* -** Perform a single step of an incremental-vacuum. If successful, -** return SQLITE_OK. If there is no work to do (and therefore no -** point in calling this function again), return SQLITE_DONE. -** -** More specificly, this function attempts to re-organize the -** database so that the last page of the file currently in use -** is no longer in use. -** -** If the nFin parameter is non-zero, this function assumes -** that the caller will keep calling incrVacuumStep() until -** it returns SQLITE_DONE or an error, and that nFin is the -** number of pages the database file will contain after this -** process is complete. If nFin is zero, it is assumed that -** incrVacuumStep() will be called a finite amount of times -** which may or may not empty the freelist. A full autovacuum -** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. -*/ -static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ - Pgno nFreeList; /* Number of pages still on the free-list */ - int rc; - - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( iLastPg>nFin ); - - if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ - u8 eType; - Pgno iPtrPage; - - nFreeList = get4byte(&pBt->pPage1->aData[36]); - if( nFreeList==0 ){ - return SQLITE_DONE; - } - - rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( eType==PTRMAP_ROOTPAGE ){ - return SQLITE_CORRUPT_BKPT; - } - - if( eType==PTRMAP_FREEPAGE ){ - if( nFin==0 ){ - /* Remove the page from the files free-list. This is not required - ** if nFin is non-zero. In that case, the free-list will be - ** truncated to zero after this function returns, so it doesn't - ** matter if it still contains some garbage entries. - */ - Pgno iFreePg; - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( iFreePg==iLastPg ); - releasePage(pFreePg); - } - } else { - Pgno iFreePg; /* Index of free page to move pLastPg to */ - MemPage *pLastPg; - - rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* If nFin is zero, this loop runs exactly once and page pLastPg - ** is swapped with the first free page pulled off the free list. - ** - ** On the other hand, if nFin is greater than zero, then keep - ** looping until a free-page located within the first nFin pages - ** of the file is found. - */ - do { - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0); - if( rc!=SQLITE_OK ){ - releasePage(pLastPg); - return rc; - } - releasePage(pFreePg); - }while( nFin!=0 && iFreePg>nFin ); - assert( iFreePgpDbPage); - if( rc==SQLITE_OK ){ - rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0); - } - releasePage(pLastPg); - if( rc!=SQLITE_OK ){ - return rc; - } - } - } - - if( nFin==0 ){ - iLastPg--; - while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ - if( PTRMAP_ISPAGE(pBt, iLastPg) ){ - MemPage *pPg; - rc = btreeGetPage(pBt, iLastPg, &pPg, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite4PagerWrite(pPg->pDbPage); - releasePage(pPg); - if( rc!=SQLITE_OK ){ - return rc; - } - } - iLastPg--; - } - sqlite4PagerTruncateImage(pBt->pPager, iLastPg); - pBt->nPage = iLastPg; - } - return SQLITE_OK; -} - -/* -** A write-transaction must be opened before calling this function. -** It performs a single unit of work towards an incremental vacuum. -** -** If the incremental vacuum is finished after this function has run, -** SQLITE_DONE is returned. If it is not finished, but no error occurred, -** SQLITE_OK is returned. Otherwise an SQLite error code. -*/ -int sqlite4BtreeIncrVacuum(Btree *p){ - int rc; - BtShared *pBt = p->pBt; - - sqlite4BtreeEnter(p); - assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); - if( !pBt->autoVacuum ){ - rc = SQLITE_DONE; - }else{ - invalidateAllOverflowCache(pBt); - rc = incrVacuumStep(pBt, 0, btreePagecount(pBt)); - if( rc==SQLITE_OK ){ - rc = sqlite4PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[28], pBt->nPage); - } - } - sqlite4BtreeLeave(p); - return rc; -} - -/* -** This routine is called prior to sqlite4PagerCommit when a transaction -** is commited for an auto-vacuum database. -** -** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages -** the database file should be truncated to during the commit process. -** i.e. the database has been reorganized so that only the first *pnTrunc -** pages are in use. -*/ -static int autoVacuumCommit(BtShared *pBt){ - int rc = SQLITE_OK; - Pager *pPager = pBt->pPager; - VVA_ONLY( int nRef = sqlite4PagerRefcount(pPager) ); - - assert( sqlite4_mutex_held(pBt->mutex) ); - invalidateAllOverflowCache(pBt); - assert(pBt->autoVacuum); - if( !pBt->incrVacuum ){ - Pgno nFin; /* Number of pages in database after autovacuuming */ - Pgno nFree; /* Number of pages on the freelist initially */ - Pgno nPtrmap; /* Number of PtrMap pages to be freed */ - Pgno iFree; /* The next page to be freed */ - int nEntry; /* Number of entries on one ptrmap page */ - Pgno nOrig; /* Database size before freeing */ - - nOrig = btreePagecount(pBt); - if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ - /* It is not possible to create a database for which the final page - ** is either a pointer-map page or the pending-byte page. If one - ** is encountered, this indicates corruption. - */ - return SQLITE_CORRUPT_BKPT; - } - - nFree = get4byte(&pBt->pPage1->aData[36]); - nEntry = pBt->usableSize/5; - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; - nFin = nOrig - nFree - nPtrmap; - if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinnOrig ) return SQLITE_CORRUPT_BKPT; - - for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){ - rc = incrVacuumStep(pBt, nFin, iFree); - } - if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ - rc = sqlite4PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[32], 0); - put4byte(&pBt->pPage1->aData[36], 0); - put4byte(&pBt->pPage1->aData[28], nFin); - sqlite4PagerTruncateImage(pBt->pPager, nFin); - pBt->nPage = nFin; - } - if( rc!=SQLITE_OK ){ - sqlite4PagerRollback(pPager); - } - } - - assert( nRef==sqlite4PagerRefcount(pPager) ); - return rc; -} - -#else /* ifndef SQLITE_OMIT_AUTOVACUUM */ -# define setChildPtrmaps(x) SQLITE_OK -#endif - -/* -** This routine does the first phase of a two-phase commit. This routine -** causes a rollback journal to be created (if it does not already exist) -** and populated with enough information so that if a power loss occurs -** the database can be restored to its original state by playing back -** the journal. Then the contents of the journal are flushed out to -** the disk. After the journal is safely on oxide, the changes to the -** database are written into the database file and flushed to oxide. -** At the end of this call, the rollback journal still exists on the -** disk and we are still holding all locks, so the transaction has not -** committed. See sqlite4BtreeCommitPhaseTwo() for the second phase of the -** commit process. -** -** This call is a no-op if no write-transaction is currently active on pBt. -** -** Otherwise, sync the database file for the btree pBt. zMaster points to -** the name of a master journal file that should be written into the -** individual journal file, or is NULL, indicating no master journal file -** (single database transaction). -** -** When this is called, the master journal should already have been -** created, populated with this journal pointer and synced to disk. -** -** Once this is routine has returned, the only thing required to commit -** the write-transaction for this database file is to delete the journal. -*/ -int sqlite4BtreeCommitPhaseOne(Btree *p, const char *zMaster){ - int rc = SQLITE_OK; - if( p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - sqlite4BtreeEnter(p); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - rc = autoVacuumCommit(pBt); - if( rc!=SQLITE_OK ){ - sqlite4BtreeLeave(p); - return rc; - } - } -#endif - rc = sqlite4PagerCommitPhaseOne(pBt->pPager, zMaster, 0); - sqlite4BtreeLeave(p); - } - return rc; -} - -/* -** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() -** at the conclusion of a transaction. -*/ -static void btreeEndTransaction(Btree *p){ - BtShared *pBt = p->pBt; - assert( sqlite4BtreeHoldsMutex(p) ); - - btreeClearHasContent(pBt); - if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ - /* If there are other active statements that belong to this database - ** handle, downgrade to a read-only transaction. The other statements - ** may still be reading from the database. */ - downgradeAllSharedCacheTableLocks(p); - p->inTrans = TRANS_READ; - }else{ - /* If the handle had any kind of transaction open, decrement the - ** transaction count of the shared btree. If the transaction count - ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() - ** call below will unlock the pager. */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - /* Set the current transaction state to TRANS_NONE and unlock the - ** pager if this call closed the only read or write transaction. */ - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - } - - btreeIntegrity(p); -} - -/* -** Commit the transaction currently in progress. -** -** This routine implements the second phase of a 2-phase commit. The -** sqlite4BtreeCommitPhaseOne() routine does the first phase and should -** be invoked prior to calling this routine. The sqlite4BtreeCommitPhaseOne() -** routine did all the work of writing information out to disk and flushing the -** contents so that they are written onto the disk platter. All this -** routine has to do is delete or truncate or zero the header in the -** the rollback journal (which causes the transaction to commit) and -** drop locks. -** -** Normally, if an error occurs while the pager layer is attempting to -** finalize the underlying journal file, this function returns an error and -** the upper layer will attempt a rollback. However, if the second argument -** is non-zero then this b-tree transaction is part of a multi-file -** transaction. In this case, the transaction has already been committed -** (by deleting a master journal file) and the caller will ignore this -** functions return code. So, even if an error occurs in the pager layer, -** reset the b-tree objects internal state to indicate that the write -** transaction has been closed. This is quite safe, as the pager will have -** transitioned to the error state. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -int sqlite4BtreeCommitPhaseTwo(Btree *p, int bCleanup){ - - if( p->inTrans==TRANS_NONE ) return SQLITE_OK; - sqlite4BtreeEnter(p); - btreeIntegrity(p); - - /* If the handle has a write-transaction open, commit the shared-btrees - ** transaction and set the shared state to TRANS_READ. - */ - if( p->inTrans==TRANS_WRITE ){ - int rc; - BtShared *pBt = p->pBt; - assert( pBt->inTransaction==TRANS_WRITE ); - assert( pBt->nTransaction>0 ); - rc = sqlite4PagerCommitPhaseTwo(pBt->pPager); - if( rc!=SQLITE_OK && bCleanup==0 ){ - sqlite4BtreeLeave(p); - return rc; - } - pBt->inTransaction = TRANS_READ; - } - - btreeEndTransaction(p); - sqlite4BtreeLeave(p); - return SQLITE_OK; -} - -/* -** Do both phases of a commit. -*/ -int sqlite4BtreeCommit(Btree *p){ - int rc; - sqlite4BtreeEnter(p); - rc = sqlite4BtreeCommitPhaseOne(p, 0); - if( rc==SQLITE_OK ){ - rc = sqlite4BtreeCommitPhaseTwo(p, 0); - } - sqlite4BtreeLeave(p); - return rc; -} - -#ifndef NDEBUG -/* -** Return the number of write-cursors open on this handle. This is for use -** in assert() expressions, so it is only compiled if NDEBUG is not -** defined. -** -** For the purposes of this routine, a write-cursor is any cursor that -** is capable of writing to the databse. That means the cursor was -** originally opened for writing and the cursor has not be disabled -** by having its state changed to CURSOR_FAULT. -*/ -static int countWriteCursors(BtShared *pBt){ - BtCursor *pCur; - int r = 0; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++; - } - return r; -} -#endif - -/* -** This routine sets the state to CURSOR_FAULT and the error -** code to errCode for every cursor on BtShared that pBtree -** references. -** -** Every cursor is tripped, including cursors that belong -** to other database connections that happen to be sharing -** the cache with pBtree. -** -** This routine gets called when a rollback occurs. -** All cursors using the same cache must be tripped -** to prevent them from trying to use the btree after -** the rollback. The rollback may have deleted tables -** or moved root pages, so it is not sufficient to -** save the state of the cursor. The cursor must be -** invalidated. -*/ -void sqlite4BtreeTripAllCursors(Btree *pBtree, int errCode){ - BtCursor *p; - sqlite4BtreeEnter(pBtree); - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - int i; - sqlite4BtreeClearCursor(p); - p->eState = CURSOR_FAULT; - p->skipNext = errCode; - for(i=0; i<=p->iPage; i++){ - releasePage(p->apPage[i]); - p->apPage[i] = 0; - } - } - sqlite4BtreeLeave(pBtree); -} - -/* -** Rollback the transaction in progress. All cursors will be -** invalided by this operation. Any attempt to use a cursor -** that was open at the beginning of this operation will result -** in an error. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -int sqlite4BtreeRollback(Btree *p){ - int rc; - BtShared *pBt = p->pBt; - MemPage *pPage1; - - sqlite4BtreeEnter(p); - rc = saveAllCursors(pBt, 0, 0); -#ifndef SQLITE_OMIT_SHARED_CACHE - if( rc!=SQLITE_OK ){ - /* This is a horrible situation. An IO or malloc() error occurred whilst - ** trying to save cursor positions. If this is an automatic rollback (as - ** the result of a constraint, malloc() failure or IO error) then - ** the cache may be internally inconsistent (not contain valid trees) so - ** we cannot simply return the error to the caller. Instead, abort - ** all queries that may be using any of the cursors that failed to save. - */ - sqlite4BtreeTripAllCursors(p, rc); - } -#endif - btreeIntegrity(p); - - if( p->inTrans==TRANS_WRITE ){ - int rc2; - - assert( TRANS_WRITE==pBt->inTransaction ); - rc2 = sqlite4PagerRollback(pBt->pPager); - if( rc2!=SQLITE_OK ){ - rc = rc2; - } - - /* The rollback may have destroyed the pPage1->aData value. So - ** call btreeGetPage() on page 1 again to make - ** sure pPage1->aData is set correctly. */ - if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ - int nPage = get4byte(28+(u8*)pPage1->aData); - testcase( nPage==0 ); - if( nPage==0 ) sqlite4PagerPagecount(pBt->pPager, &nPage); - testcase( pBt->nPage!=nPage ); - pBt->nPage = nPage; - releasePage(pPage1); - } - assert( countWriteCursors(pBt)==0 ); - pBt->inTransaction = TRANS_READ; - } - - btreeEndTransaction(p); - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Start a statement subtransaction. The subtransaction can can be rolled -** back independently of the main transaction. You must start a transaction -** before starting a subtransaction. The subtransaction is ended automatically -** if the main transaction commits or rolls back. -** -** Statement subtransactions are used around individual SQL statements -** that are contained within a BEGIN...COMMIT block. If a constraint -** error occurs within the statement, the effect of that one statement -** can be rolled back without having to rollback the entire transaction. -** -** A statement sub-transaction is implemented as an anonymous savepoint. The -** value passed as the second parameter is the total number of savepoints, -** including the new anonymous savepoint, open on the B-Tree. i.e. if there -** are no active savepoints and no other statement-transactions open, -** iStatement is 1. This anonymous savepoint can be released or rolled back -** using the sqlite4BtreeSavepoint() function. -*/ -int sqlite4BtreeBeginStmt(Btree *p, int iStatement){ - int rc; - BtShared *pBt = p->pBt; - sqlite4BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( iStatement>0 ); - assert( iStatement>p->db->nSavepoint ); - assert( pBt->inTransaction==TRANS_WRITE ); - /* At the pager level, a statement transaction is a savepoint with - ** an index greater than all savepoints created explicitly using - ** SQL statements. It is illegal to open, release or rollback any - ** such savepoints while the statement transaction savepoint is active. - */ - rc = sqlite4PagerOpenSavepoint(pBt->pPager, iStatement); - sqlite4BtreeLeave(p); - return rc; -} - -/* -** The second argument to this function, op, is always SAVEPOINT_ROLLBACK -** or SAVEPOINT_RELEASE. This function either releases or rolls back the -** savepoint identified by parameter iSavepoint, depending on the value -** of op. -** -** Normally, iSavepoint is greater than or equal to zero. However, if op is -** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the -** contents of the entire transaction are rolled back. This is different -** from a normal transaction rollback, as no locks are released and the -** transaction remains open. -*/ -int sqlite4BtreeSavepoint(Btree *p, int op, int iSavepoint){ - int rc = SQLITE_OK; - if( p && p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); - sqlite4BtreeEnter(p); - rc = sqlite4PagerSavepoint(pBt->pPager, op, iSavepoint); - if( rc==SQLITE_OK ){ - if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ - pBt->nPage = 0; - } - rc = newDatabase(pBt); - pBt->nPage = get4byte(28 + pBt->pPage1->aData); - - /* The database size was written into the offset 28 of the header - ** when the transaction started, so we know that the value at offset - ** 28 is nonzero. */ - assert( pBt->nPage>0 ); - } - sqlite4BtreeLeave(p); - } - return rc; -} - -/* -** Create a new cursor for the BTree whose root is on the page -** iTable. If a read-only cursor is requested, it is assumed that -** the caller already has at least a read-only transaction open -** on the database already. If a write-cursor is requested, then -** the caller is assumed to have an open write transaction. -** -** If wrFlag==0, then the cursor can only be used for reading. -** If wrFlag==1, then the cursor can be used for reading or for -** writing if other conditions for writing are also met. These -** are the conditions that must be met in order for writing to -** be allowed: -** -** 1: The cursor must have been opened with wrFlag==1 -** -** 2: Other database connections that share the same pager cache -** but which are not in the READ_UNCOMMITTED state may not have -** cursors open with wrFlag==0 on the same table. Otherwise -** the changes made by this write cursor would be visible to -** the read cursors in the other database connection. -** -** 3: The database must be writable (not on read-only media) -** -** 4: There must be an active transaction. -** -** No checking is done to make sure that page iTable really is the -** root page of a b-tree. If it is not, then the cursor acquired -** will not work correctly. -** -** It is assumed that the sqlite4BtreeCursorZero() has been called -** on pCur to initialize the memory space prior to invoking this routine. -*/ -static int btreeCursor( - Btree *p, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to comparison function */ - BtCursor *pCur /* Space for new cursor */ -){ - BtShared *pBt = p->pBt; /* Shared b-tree handle */ - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( wrFlag==0 || wrFlag==1 ); - - /* The following assert statements verify that if this is a sharable - ** b-tree database, the connection is holding the required table locks, - ** and that no other connection has any open cursor that conflicts with - ** this lock. */ - assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) ); - assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); - - /* Assert that the caller has opened the required transaction. */ - assert( p->inTrans>TRANS_NONE ); - assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1 && pBt->pPage1->aData ); - - if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){ - return SQLITE_READONLY; - } - if( iTable==1 && btreePagecount(pBt)==0 ){ - assert( wrFlag==0 ); - iTable = 0; - } - - /* Now that no other errors can occur, finish filling in the BtCursor - ** variables and link the cursor into the BtShared list. */ - pCur->pgnoRoot = (Pgno)iTable; - pCur->iPage = -1; - pCur->pKeyInfo = pKeyInfo; - pCur->pBtree = p; - pCur->pBt = pBt; - pCur->wrFlag = (u8)wrFlag; - pCur->pNext = pBt->pCursor; - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur; - } - pBt->pCursor = pCur; - pCur->eState = CURSOR_INVALID; - pCur->cachedRowid = 0; - return SQLITE_OK; -} -int sqlite4BtreeCursor( - Btree *p, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ - BtCursor *pCur /* Write new cursor here */ -){ - int rc; - sqlite4BtreeEnter(p); - rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Return the size of a BtCursor object in bytes. -** -** This interfaces is needed so that users of cursors can preallocate -** sufficient storage to hold a cursor. The BtCursor object is opaque -** to users so they cannot do the sizeof() themselves - they must call -** this routine. -*/ -int sqlite4BtreeCursorSize(void){ - return ROUND8(sizeof(BtCursor)); -} - -/* -** Initialize memory that will be converted into a BtCursor object. -** -** The simple approach here would be to memset() the entire object -** to zero. But it turns out that the apPage[] and aiIdx[] arrays -** do not need to be zeroed and they are large, so we can save a lot -** of run-time by skipping the initialization of those elements. -*/ -void sqlite4BtreeCursorZero(BtCursor *p){ - memset(p, 0, offsetof(BtCursor, iPage)); -} - -/* -** Set the cached rowid value of every cursor in the same database file -** as pCur and having the same root page number as pCur. The value is -** set to iRowid. -** -** Only positive rowid values are considered valid for this cache. -** The cache is initialized to zero, indicating an invalid cache. -** A btree will work fine with zero or negative rowids. We just cannot -** cache zero or negative rowids, which means tables that use zero or -** negative rowids might run a little slower. But in practice, zero -** or negative rowids are very uncommon so this should not be a problem. -*/ -void sqlite4BtreeSetCachedRowid(BtCursor *pCur, sqlite4_int64 iRowid){ - BtCursor *p; - for(p=pCur->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid; - } - assert( pCur->cachedRowid==iRowid ); -} - -/* -** Return the cached rowid for the given cursor. A negative or zero -** return value indicates that the rowid cache is invalid and should be -** ignored. If the rowid cache has never before been set, then a -** zero is returned. -*/ -sqlite4_int64 sqlite4BtreeGetCachedRowid(BtCursor *pCur){ - return pCur->cachedRowid; -} - -/* -** Close a cursor. The read lock on the database file is released -** when the last cursor is closed. -*/ -int sqlite4BtreeCloseCursor(BtCursor *pCur){ - Btree *pBtree = pCur->pBtree; - if( pBtree ){ - int i; - BtShared *pBt = pCur->pBt; - sqlite4BtreeEnter(pBtree); - sqlite4BtreeClearCursor(pCur); - if( pCur->pPrev ){ - pCur->pPrev->pNext = pCur->pNext; - }else{ - pBt->pCursor = pCur->pNext; - } - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur->pPrev; - } - for(i=0; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - } - unlockBtreeIfUnused(pBt); - invalidateOverflowCache(pCur); - /* sqlite4_free(pCur); */ - sqlite4BtreeLeave(pBtree); - } - return SQLITE_OK; -} - -/* -** Make sure the BtCursor* given in the argument has a valid -** BtCursor.info structure. If it is not already valid, call -** btreeParseCell() to fill it in. -** -** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to btreeParseCell(). -** -** 2007-06-25: There is a bug in some versions of MSVC that cause the -** compiler to crash when getCellInfo() is implemented as a macro. -** But there is a measureable speed advantage to using the macro on gcc -** (when less compiler optimizations like -Os or -O0 are used and the -** compiler is not doing agressive inlining.) So we use a real function -** for MSVC and a macro for everything else. Ticket #2457. -*/ -#ifndef NDEBUG - static void assertCellInfo(BtCursor *pCur){ - CellInfo info; - int iPage = pCur->iPage; - memset(&info, 0, sizeof(info)); - btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); - assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); - } -#else - #define assertCellInfo(x) -#endif -#ifdef _MSC_VER - /* Use a real function in MSVC to work around bugs in that compiler. */ - static void getCellInfo(BtCursor *pCur){ - if( pCur->info.nSize==0 ){ - int iPage = pCur->iPage; - btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); - pCur->validNKey = 1; - }else{ - assertCellInfo(pCur); - } - } -#else /* if not _MSC_VER */ - /* Use a macro in all other compilers so that the function is inlined */ -#define getCellInfo(pCur) \ - if( pCur->info.nSize==0 ){ \ - int iPage = pCur->iPage; \ - btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ - pCur->validNKey = 1; \ - }else{ \ - assertCellInfo(pCur); \ - } -#endif /* _MSC_VER */ - -#ifndef NDEBUG /* The next routine used only within assert() statements */ -/* -** Return true if the given BtCursor is valid. A valid cursor is one -** that is currently pointing to a row in a (non-empty) table. -** This is a verification routine is used only within assert() statements. -*/ -int sqlite4BtreeCursorIsValid(BtCursor *pCur){ - return pCur && pCur->eState==CURSOR_VALID; -} -#endif /* NDEBUG */ - -/* -** Set *pSize to the size of the buffer needed to hold the value of -** the key for the current entry. If the cursor is not pointing -** to a valid entry, *pSize is set to 0. -** -** For a table with the INTKEY flag set, this routine returns the key -** itself, not the number of bytes in the key. -** -** The caller must position the cursor prior to invoking this routine. -** -** This routine cannot fail. It always returns SQLITE_OK. -*/ -int sqlite4BtreeKeySize(BtCursor *pCur, i64 *pSize){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState!=CURSOR_VALID ){ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nKey; - } - return SQLITE_OK; -} - -/* -** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. -** -** The caller must guarantee that the cursor is pointing to a non-NULL -** valid entry. In other words, the calling procedure must guarantee -** that the cursor has Cursor.eState==CURSOR_VALID. -** -** Failure is not possible. This function always returns SQLITE_OK. -** It might just as well be a procedure (returning void) but we continue -** to return an integer result code for historical reasons. -*/ -int sqlite4BtreeDataSize(BtCursor *pCur, u32 *pSize){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - getCellInfo(pCur); - *pSize = pCur->info.nData; - return SQLITE_OK; -} - -/* -** Given the page number of an overflow page in the database (parameter -** ovfl), this function finds the page number of the next page in the -** linked list of overflow pages. If possible, it uses the auto-vacuum -** pointer-map data instead of reading the content of page ovfl to do so. -** -** If an error occurs an SQLite error code is returned. Otherwise: -** -** The page number of the next overflow page in the linked list is -** written to *pPgnoNext. If page ovfl is the last page in its linked -** list, *pPgnoNext is set to zero. -** -** If ppPage is not NULL, and a reference to the MemPage object corresponding -** to page number pOvfl was obtained, then *ppPage is set to point to that -** reference. It is the responsibility of the caller to call releasePage() -** on *ppPage to free the reference. In no reference was obtained (because -** the pointer-map was used to obtain the value for *pPgnoNext), then -** *ppPage is set to zero. -*/ -static int getOverflowPage( - BtShared *pBt, /* The database file */ - Pgno ovfl, /* Current overflow page number */ - MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ - Pgno *pPgnoNext /* OUT: Next overflow page number */ -){ - Pgno next = 0; - MemPage *pPage = 0; - int rc = SQLITE_OK; - - assert( sqlite4_mutex_held(pBt->mutex) ); - assert(pPgnoNext); - -#ifndef SQLITE_OMIT_AUTOVACUUM - /* Try to find the next page in the overflow list using the - ** autovacuum pointer-map pages. Guess that the next page in - ** the overflow list is page number (ovfl+1). If that guess turns - ** out to be wrong, fall back to loading the data of page - ** number ovfl to determine the next page number. - */ - if( pBt->autoVacuum ){ - Pgno pgno; - Pgno iGuess = ovfl+1; - u8 eType; - - while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ - iGuess++; - } - - if( iGuess<=btreePagecount(pBt) ){ - rc = ptrmapGet(pBt, iGuess, &eType, &pgno); - if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ - next = iGuess; - rc = SQLITE_DONE; - } - } - } -#endif - - assert( next==0 || rc==SQLITE_DONE ); - if( rc==SQLITE_OK ){ - rc = btreeGetPage(pBt, ovfl, &pPage, 0); - assert( rc==SQLITE_OK || pPage==0 ); - if( rc==SQLITE_OK ){ - next = get4byte(pPage->aData); - } - } - - *pPgnoNext = next; - if( ppPage ){ - *ppPage = pPage; - }else{ - releasePage(pPage); - } - return (rc==SQLITE_DONE ? SQLITE_OK : rc); -} - -/* -** Copy data from a buffer to a page, or from a page to a buffer. -** -** pPayload is a pointer to data stored on database page pDbPage. -** If argument eOp is false, then nByte bytes of data are copied -** from pPayload to the buffer pointed at by pBuf. If eOp is true, -** then sqlite4PagerWrite() is called on pDbPage and nByte bytes -** of data are copied from the buffer pBuf to pPayload. -** -** SQLITE_OK is returned on success, otherwise an error code. -*/ -static int copyPayload( - void *pPayload, /* Pointer to page data */ - void *pBuf, /* Pointer to buffer */ - int nByte, /* Number of bytes to copy */ - int eOp, /* 0 -> copy from page, 1 -> copy to page */ - DbPage *pDbPage /* Page containing pPayload */ -){ - if( eOp ){ - /* Copy data from buffer to page (a write operation) */ - int rc = sqlite4PagerWrite(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - memcpy(pPayload, pBuf, nByte); - }else{ - /* Copy data from page to buffer (a read operation) */ - memcpy(pBuf, pPayload, nByte); - } - return SQLITE_OK; -} - -/* -** This function is used to read or overwrite payload information -** for the entry that the pCur cursor is pointing to. If the eOp -** parameter is 0, this is a read operation (data copied into -** buffer pBuf). If it is non-zero, a write (data copied from -** buffer pBuf). -** -** A total of "amt" bytes are read or written beginning at "offset". -** Data is read to or from the buffer pBuf. -** -** The content being read or written might appear on the main page -** or be scattered out on multiple overflow pages. -** -** If the BtCursor.isIncrblobHandle flag is set, and the current -** cursor entry uses one or more overflow pages, this function -** allocates space for and lazily popluates the overflow page-list -** cache array (BtCursor.aOverflow). Subsequent calls use this -** cache to make seeking to the supplied offset more efficient. -** -** Once an overflow page-list cache has been allocated, it may be -** invalidated if some other cursor writes to the same table, or if -** the cursor is moved to a different row. Additionally, in auto-vacuum -** mode, the following events may invalidate an overflow page-list cache. -** -** * An incremental vacuum, -** * A commit in auto_vacuum="full" mode, -** * Creating a table (may require moving an overflow page). -*/ -static int accessPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - u32 offset, /* Begin reading this far into payload */ - u32 amt, /* Read this many bytes */ - unsigned char *pBuf, /* Write the bytes into this buffer */ - int eOp /* zero to read. non-zero to write. */ -){ - unsigned char *aPayload; - int rc = SQLITE_OK; - u32 nKey; - int iIdx = 0; - MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */ - BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ - - assert( pPage ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->aiIdx[pCur->iPage]nCell ); - assert( cursorHoldsMutex(pCur) ); - - getCellInfo(pCur); - aPayload = pCur->info.pCell + pCur->info.nHeader; - nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); - - if( NEVER(offset+amt > nKey+pCur->info.nData) - || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] - ){ - /* Trying to read or write past the end of the data is an error */ - return SQLITE_CORRUPT_BKPT; - } - - /* Check if data must be read/written to/from the btree page itself. */ - if( offsetinfo.nLocal ){ - int a = amt; - if( a+offset>pCur->info.nLocal ){ - a = pCur->info.nLocal - offset; - } - rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); - offset = 0; - pBuf += a; - amt -= a; - }else{ - offset -= pCur->info.nLocal; - } - - if( rc==SQLITE_OK && amt>0 ){ - const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ - Pgno nextPage; - - nextPage = get4byte(&aPayload[pCur->info.nLocal]); - -#ifndef SQLITE_OMIT_INCRBLOB - /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] - ** has not been allocated, allocate it now. The array is sized at - ** one entry for each overflow page in the overflow chain. The - ** page number of the first overflow page is stored in aOverflow[0], - ** etc. A value of 0 in the aOverflow[] array means "not yet known" - ** (the cache is lazily populated). - */ - if( pCur->isIncrblobHandle && !pCur->aOverflow ){ - int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - pCur->aOverflow = (Pgno *)sqlite4MallocZero(sizeof(Pgno)*nOvfl); - /* nOvfl is always positive. If it were zero, fetchPayload would have - ** been used instead of this routine. */ - if( ALWAYS(nOvfl) && !pCur->aOverflow ){ - rc = SQLITE_NOMEM; - } - } - - /* If the overflow page-list cache has been allocated and the - ** entry for the first required overflow page is valid, skip - ** directly to it. - */ - if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){ - iIdx = (offset/ovflSize); - nextPage = pCur->aOverflow[iIdx]; - offset = (offset%ovflSize); - } -#endif - - for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){ - -#ifndef SQLITE_OMIT_INCRBLOB - /* If required, populate the overflow page-list cache. */ - if( pCur->aOverflow ){ - assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage); - pCur->aOverflow[iIdx] = nextPage; - } -#endif - - if( offset>=ovflSize ){ - /* The only reason to read this page is to obtain the page - ** number for the next page in the overflow chain. The page - ** data is not required. So first try to lookup the overflow - ** page-list cache, if any, then fall back to the getOverflowPage() - ** function. - */ -#ifndef SQLITE_OMIT_INCRBLOB - if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){ - nextPage = pCur->aOverflow[iIdx+1]; - } else -#endif - rc = getOverflowPage(pBt, nextPage, 0, &nextPage); - offset -= ovflSize; - }else{ - /* Need to read this page properly. It contains some of the - ** range of data that is being read (eOp==0) or written (eOp!=0). - */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ - sqlite4_file *fd; -#endif - int a = amt; - if( a + offset > ovflSize ){ - a = ovflSize - offset; - } - -#ifdef SQLITE_DIRECT_OVERFLOW_READ - /* If all the following are true: - ** - ** 1) this is a read operation, and - ** 2) data is required from the start of this overflow page, and - ** 3) the database is file-backed, and - ** 4) there is no open write-transaction, and - ** 5) the database is not a WAL database, - ** - ** then data can be read directly from the database file into the - ** output buffer, bypassing the page-cache altogether. This speeds - ** up loading large records that span many overflow pages. - */ - if( eOp==0 /* (1) */ - && offset==0 /* (2) */ - && pBt->inTransaction==TRANS_READ /* (4) */ - && (fd = sqlite4PagerFile(pBt->pPager))->pMethods /* (3) */ - && pBt->pPage1->aData[19]==0x01 /* (5) */ - ){ - u8 aSave[4]; - u8 *aWrite = &pBuf[-4]; - memcpy(aSave, aWrite, 4); - rc = sqlite4OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); - nextPage = get4byte(aWrite); - memcpy(aWrite, aSave, 4); - }else -#endif - - { - DbPage *pDbPage; - rc = sqlite4PagerGet(pBt->pPager, nextPage, &pDbPage); - if( rc==SQLITE_OK ){ - aPayload = sqlite4PagerGetData(pDbPage); - nextPage = get4byte(aPayload); - rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); - sqlite4PagerUnref(pDbPage); - offset = 0; - } - } - amt -= a; - pBuf += a; - } - } - } - - if( rc==SQLITE_OK && amt>0 ){ - return SQLITE_CORRUPT_BKPT; - } - return rc; -} - -/* -** Read part of the key associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** The caller must ensure that pCur is pointing to a valid row -** in the table. -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -int sqlite4BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); -} - -/* -** Read part of the data associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -int sqlite4BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - int rc; - -#ifndef SQLITE_OMIT_INCRBLOB - if ( pCur->eState==CURSOR_INVALID ){ - return SQLITE_ABORT; - } -#endif - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, pBuf, 0); - } - return rc; -} - -/* -** Return a pointer to payload information from the entry that the -** pCur cursor is pointing to. The pointer is to the beginning of -** the key if skipKey==0 and it points to the beginning of data if -** skipKey==1. The number of bytes of available key/data is written -** into *pAmt. If *pAmt==0, then the value returned will not be -** a valid pointer. -** -** This routine is an optimization. It is common for the entire key -** and data to fit on the local page and for there to be no overflow -** pages. When that is so, this routine can be used to access the -** key and data without making a copy. If the key and/or data spills -** onto overflow pages, then accessPayload() must be used to reassemble -** the key/data and copy it into a preallocated buffer. -** -** The pointer returned by this routine looks directly into the cached -** page of the database. The data might change or move the next time -** any btree routine is called. -*/ -static const unsigned char *fetchPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - int *pAmt, /* Write the number of available bytes here */ - int skipKey /* read beginning at data if this is true */ -){ - unsigned char *aPayload; - MemPage *pPage; - u32 nKey; - u32 nLocal; - - assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); - assert( pCur->eState==CURSOR_VALID ); - assert( cursorHoldsMutex(pCur) ); - pPage = pCur->apPage[pCur->iPage]; - assert( pCur->aiIdx[pCur->iPage]nCell ); - if( NEVER(pCur->info.nSize==0) ){ - btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], - &pCur->info); - } - aPayload = pCur->info.pCell; - aPayload += pCur->info.nHeader; - if( pPage->intKey ){ - nKey = 0; - }else{ - nKey = (int)pCur->info.nKey; - } - if( skipKey ){ - aPayload += nKey; - nLocal = pCur->info.nLocal - nKey; - }else{ - nLocal = pCur->info.nLocal; - assert( nLocal<=nKey ); - } - *pAmt = nLocal; - return aPayload; -} - - -/* -** For the entry that cursor pCur is point to, return as -** many bytes of the key or data as are available on the local -** b-tree page. Write the number of available bytes into *pAmt. -** -** The pointer returned is ephemeral. The key/data may move -** or be destroyed on the next call to any Btree routine, -** including calls from other threads against the same cache. -** Hence, a mutex on the BtShared should be held prior to calling -** this routine. -** -** These routines is used to get quick access to key and data -** in the common case where no overflow pages are used. -*/ -const void *sqlite4BtreeKeyFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 0); - } - return p; -} -const void *sqlite4BtreeDataFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 1); - } - return p; -} - - -/* -** Move the cursor down to a new child page. The newPgno argument is the -** page number of the child page to move to. -** -** This function returns SQLITE_CORRUPT if the page-header flags field of -** the new child page does not match the flags field of the parent (i.e. -** if an intkey page appears to be the parent of a non-intkey page, or -** vice-versa). -*/ -static int moveToChild(BtCursor *pCur, u32 newPgno){ - int rc; - int i = pCur->iPage; - MemPage *pNewPage; - BtShared *pBt = pCur->pBt; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPageiPage>=(BTCURSOR_MAX_DEPTH-1) ){ - return SQLITE_CORRUPT_BKPT; - } - rc = getAndInitPage(pBt, newPgno, &pNewPage); - if( rc ) return rc; - pCur->apPage[i+1] = pNewPage; - pCur->aiIdx[i+1] = 0; - pCur->iPage++; - - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - return SQLITE_OK; -} - -#if 0 -/* -** Page pParent is an internal (non-leaf) tree page. This function -** asserts that page number iChild is the left-child if the iIdx'th -** cell in page pParent. Or, if iIdx is equal to the total number of -** cells in pParent, that page number iChild is the right-child of -** the page. -*/ -static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ - assert( iIdx<=pParent->nCell ); - if( iIdx==pParent->nCell ){ - assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); - }else{ - assert( get4byte(findCell(pParent, iIdx))==iChild ); - } -} -#else -# define assertParentIndex(x,y,z) -#endif - -/* -** Move the cursor up to the parent page. -** -** pCur->idx is set to the cell index that contains the pointer -** to the page we are coming from. If we are coming from the -** right-most child page then pCur->idx is set to one more than -** the largest cell index. -*/ -static void moveToParent(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>0 ); - assert( pCur->apPage[pCur->iPage] ); - - /* UPDATE: It is actually possible for the condition tested by the assert - ** below to be untrue if the database file is corrupt. This can occur if - ** one cursor has modified page pParent while a reference to it is held - ** by a second cursor. Which can only happen if a single page is linked - ** into more than one b-tree structure in a corrupt database. */ -#if 0 - assertParentIndex( - pCur->apPage[pCur->iPage-1], - pCur->aiIdx[pCur->iPage-1], - pCur->apPage[pCur->iPage]->pgno - ); -#endif - testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); - - releasePage(pCur->apPage[pCur->iPage]); - pCur->iPage--; - pCur->info.nSize = 0; - pCur->validNKey = 0; -} - -/* -** Move the cursor to point to the root page of its b-tree structure. -** -** If the table has a virtual root page, then the cursor is moved to point -** to the virtual root page instead of the actual root page. A table has a -** virtual root page when the actual root page contains no cells and a -** single child page. This can only happen with the table rooted at page 1. -** -** If the b-tree structure is empty, the cursor state is set to -** CURSOR_INVALID. Otherwise, the cursor is set to point to the first -** cell located on the root (or virtual root) page and the cursor state -** is set to CURSOR_VALID. -** -** If this function returns successfully, it may be assumed that the -** page-header flags indicate that the [virtual] root-page is the expected -** kind of b-tree page (i.e. if when opening the cursor the caller did not -** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, -** indicating a table b-tree, or if the caller did specify a KeyInfo -** structure the flags byte is set to 0x02 or 0x0A, indicating an index -** b-tree). -*/ -static int moveToRoot(BtCursor *pCur){ - MemPage *pRoot; - int rc = SQLITE_OK; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - - assert( cursorHoldsMutex(pCur) ); - assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); - if( pCur->eState>=CURSOR_REQUIRESEEK ){ - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - sqlite4BtreeClearCursor(pCur); - } - - if( pCur->iPage>=0 ){ - int i; - for(i=1; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - } - pCur->iPage = 0; - }else if( pCur->pgnoRoot==0 ){ - pCur->eState = CURSOR_INVALID; - return SQLITE_OK; - }else{ - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); - if( rc!=SQLITE_OK ){ - pCur->eState = CURSOR_INVALID; - return rc; - } - pCur->iPage = 0; - - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is - ** NULL, the caller expects a table b-tree. If this is not the case, - ** return an SQLITE_CORRUPT error. */ - assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); - if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - } - - /* Assert that the root page is of the correct type. This must be the - ** case as the call to this function that loaded the root-page (either - ** this call or a previous invocation) would have detected corruption - ** if the assumption were not true, and it is not possible for the flags - ** byte to have been modified while this cursor is holding a reference - ** to the page. */ - pRoot = pCur->apPage[0]; - assert( pRoot->pgno==pCur->pgnoRoot ); - assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); - - pCur->aiIdx[0] = 0; - pCur->info.nSize = 0; - pCur->atLast = 0; - pCur->validNKey = 0; - - if( pRoot->nCell==0 && !pRoot->leaf ){ - Pgno subpage; - if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; - subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - pCur->eState = CURSOR_VALID; - rc = moveToChild(pCur, subpage); - }else{ - pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID); - } - return rc; -} - -/* -** Move the cursor down to the left-most leaf entry beneath the -** entry to which it is currently pointing. -** -** The left-most leaf is the one with the smallest key - the first -** in ascending order. -*/ -static int moveToLeftmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ - assert( pCur->aiIdx[pCur->iPage]nCell ); - pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage])); - rc = moveToChild(pCur, pgno); - } - return rc; -} - -/* -** Move the cursor down to the right-most leaf entry beneath the -** page to which it is currently pointing. Notice the difference -** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() -** finds the left-most entry beneath the *entry* whereas moveToRightmost() -** finds the right-most entry beneath the *page*. -** -** The right-most entry is the one with the largest key - the last -** key in ascending order. -*/ -static int moveToRightmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage = 0; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - pCur->aiIdx[pCur->iPage] = pPage->nCell; - rc = moveToChild(pCur, pgno); - } - if( rc==SQLITE_OK ){ - pCur->aiIdx[pCur->iPage] = pPage->nCell-1; - pCur->info.nSize = 0; - pCur->validNKey = 0; - } - return rc; -} - -/* Move the cursor to the first entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -int sqlite4BtreeFirst(BtCursor *pCur, int *pRes){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - if( pCur->eState==CURSOR_INVALID ){ - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - *pRes = 1; - }else{ - assert( pCur->apPage[pCur->iPage]->nCell>0 ); - *pRes = 0; - rc = moveToLeftmost(pCur); - } - } - return rc; -} - -/* Move the cursor to the last entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -int sqlite4BtreeLast(BtCursor *pCur, int *pRes){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - - /* If the cursor already points to the last entry, this is a no-op. */ - if( CURSOR_VALID==pCur->eState && pCur->atLast ){ -#ifdef SQLITE_DEBUG - /* This block serves to assert() that the cursor really does point - ** to the last entry in the b-tree. */ - int ii; - for(ii=0; iiiPage; ii++){ - assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); - } - assert( pCur->aiIdx[pCur->iPage]==pCur->apPage[pCur->iPage]->nCell-1 ); - assert( pCur->apPage[pCur->iPage]->leaf ); -#endif - return SQLITE_OK; - } - - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - if( CURSOR_INVALID==pCur->eState ){ - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - *pRes = 1; - }else{ - assert( pCur->eState==CURSOR_VALID ); - *pRes = 0; - rc = moveToRightmost(pCur); - pCur->atLast = rc==SQLITE_OK ?1:0; - } - } - return rc; -} - -/* Move the cursor so that it points to an entry near the key -** specified by pIdxKey or intKey. Return a success code. -** -** For INTKEY tables, the intKey parameter is used. pIdxKey -** must be NULL. For index tables, pIdxKey is used and intKey -** is ignored. -** -** If an exact match is not found, then the cursor is always -** left pointing at a leaf page which would hold the entry if it -** were present. The cursor might point to an entry that comes -** before or after the key. -** -** An integer is written into *pRes which is the result of -** comparing the key with the entry to which the cursor is -** pointing. The meaning of the integer written into -** *pRes is as follows: -** -** *pRes<0 The cursor is left pointing at an entry that -** is smaller than intKey/pIdxKey or if the table is empty -** and the cursor is therefore left point to nothing. -** -** *pRes==0 The cursor is left pointing at an entry that -** exactly matches intKey/pIdxKey. -** -** *pRes>0 The cursor is left pointing at an entry that -** is larger than intKey/pIdxKey. -** -*/ -int sqlite4BtreeMovetoUnpacked( - BtCursor *pCur, /* The cursor to be moved */ - UnpackedRecord *pIdxKey, /* Unpacked index key */ - i64 intKey, /* The table key */ - int biasRight, /* If true, bias the search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - assert( pRes ); - assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); - - /* If the cursor is already positioned at the point we are trying - ** to move to, then just return without doing any work */ - if( pCur->eState==CURSOR_VALID && pCur->validNKey - && pCur->apPage[0]->intKey - ){ - if( pCur->info.nKey==intKey ){ - *pRes = 0; - return SQLITE_OK; - } - if( pCur->atLast && pCur->info.nKeypgnoRoot==0 || pCur->apPage[pCur->iPage] ); - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit ); - assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 ); - if( pCur->eState==CURSOR_INVALID ){ - *pRes = -1; - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - return SQLITE_OK; - } - assert( pCur->apPage[0]->intKey || pIdxKey ); - for(;;){ - int lwr, upr, idx; - Pgno chldPg; - MemPage *pPage = pCur->apPage[pCur->iPage]; - int c; - - /* pPage->nCell must be greater than zero. If this is the root-page - ** the cursor would have been INVALID above and this for(;;) loop - ** not run. If this is not the root-page, then the moveToChild() routine - ** would have already detected db corruption. Similarly, pPage must - ** be the right kind (index or table) of b-tree page. Otherwise - ** a moveToChild() or moveToRoot() call would have detected corruption. */ - assert( pPage->nCell>0 ); - assert( pPage->intKey==(pIdxKey==0) ); - lwr = 0; - upr = pPage->nCell-1; - if( biasRight ){ - pCur->aiIdx[pCur->iPage] = (u16)(idx = upr); - }else{ - pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2); - } - for(;;){ - u8 *pCell; /* Pointer to current cell in pPage */ - - assert( idx==pCur->aiIdx[pCur->iPage] ); - pCur->info.nSize = 0; - pCell = findCell(pPage, idx) + pPage->childPtrSize; - if( pPage->intKey ){ - i64 nCellKey; - if( pPage->hasData ){ - u32 dummy; - pCell += getVarint32(pCell, dummy); - } - getVarint(pCell, (u64*)&nCellKey); - if( nCellKey==intKey ){ - c = 0; - }else if( nCellKeyintKey ); - c = +1; - } - pCur->validNKey = 1; - pCur->info.nKey = nCellKey; - }else{ - /* The maximum supported page-size is 65536 bytes. This means that - ** the maximum number of record bytes stored on an index B-Tree - ** page is less than 16384 bytes and may be stored as a 2-byte - ** varint. This information is used to attempt to avoid parsing - ** the entire cell by checking for the cases where the record is - ** stored entirely within the b-tree page by inspecting the first - ** 2 bytes of the cell. - */ - int nCell = pCell[0]; - if( nCell<=pPage->max1bytePayload - /* && (pCell+nCell)aDataEnd */ - ){ - /* This branch runs if the record-size field of the cell is a - ** single byte varint and the record fits entirely on the main - ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); - c = sqlite4VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); - }else if( !(pCell[1] & 0x80) - && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal - /* && (pCell+nCell+2)<=pPage->aDataEnd */ - ){ - /* The record-size field is a 2 byte varint and the record - ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); - c = sqlite4VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); - }else{ - /* The record flows over onto one or more overflow pages. In - ** this case the whole cell needs to be parsed, a buffer allocated - ** and accessPayload() used to retrieve the record into the - ** buffer before VdbeRecordCompare() can be called. */ - void *pCellKey; - u8 * const pCellBody = pCell - pPage->childPtrSize; - btreeParseCellPtr(pPage, pCellBody, &pCur->info); - nCell = (int)pCur->info.nKey; - pCellKey = sqlite4Malloc( nCell ); - if( pCellKey==0 ){ - rc = SQLITE_NOMEM; - goto moveto_finish; - } - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); - if( rc ){ - sqlite4_free(pCellKey); - goto moveto_finish; - } - c = sqlite4VdbeRecordCompare(nCell, pCellKey, pIdxKey); - sqlite4_free(pCellKey); - } - } - if( c==0 ){ - if( pPage->intKey && !pPage->leaf ){ - lwr = idx; - break; - }else{ - *pRes = 0; - rc = SQLITE_OK; - goto moveto_finish; - } - } - if( c<0 ){ - lwr = idx+1; - }else{ - upr = idx-1; - } - if( lwr>upr ){ - break; - } - pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); - } - assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); - assert( pPage->isInit ); - if( pPage->leaf ){ - chldPg = 0; - }else if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - if( chldPg==0 ){ - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - *pRes = c; - rc = SQLITE_OK; - goto moveto_finish; - } - pCur->aiIdx[pCur->iPage] = (u16)lwr; - pCur->info.nSize = 0; - pCur->validNKey = 0; - rc = moveToChild(pCur, chldPg); - if( rc ) goto moveto_finish; - } -moveto_finish: - return rc; -} - - -/* -** Return TRUE if the cursor is not pointing at an entry of the table. -** -** TRUE will be returned after a call to sqlite4BtreeNext() moves -** past the last entry in the table or sqlite4BtreePrev() moves past -** the first entry. TRUE is also returned if the table is empty. -*/ -int sqlite4BtreeEof(BtCursor *pCur){ - /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries - ** have been deleted? This API will need to change to return an error code - ** as well as the boolean result value. - */ - return (CURSOR_VALID!=pCur->eState); -} - -/* -** Advance the cursor to the next entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the last entry in the database before -** this routine was called, then set *pRes=1. -*/ -int sqlite4BtreeNext(BtCursor *pCur, int *pRes){ - int rc; - int idx; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pRes!=0 ); - if( CURSOR_INVALID==pCur->eState ){ - *pRes = 1; - return SQLITE_OK; - } - if( pCur->skipNext>0 ){ - pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; - - pPage = pCur->apPage[pCur->iPage]; - idx = ++pCur->aiIdx[pCur->iPage]; - assert( pPage->isInit ); - - /* If the database file is corrupt, it is possible for the value of idx - ** to be invalid here. This can only occur if a second cursor modifies - ** the page while cursor pCur is holding a reference to it. Which can - ** only happen if the database is corrupt in such a way as to link the - ** page into more than one b-tree structure. */ - testcase( idx>pPage->nCell ); - - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( idx>=pPage->nCell ){ - if( !pPage->leaf ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - if( rc ) return rc; - rc = moveToLeftmost(pCur); - *pRes = 0; - return rc; - } - do{ - if( pCur->iPage==0 ){ - *pRes = 1; - pCur->eState = CURSOR_INVALID; - return SQLITE_OK; - } - moveToParent(pCur); - pPage = pCur->apPage[pCur->iPage]; - }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell ); - *pRes = 0; - if( pPage->intKey ){ - rc = sqlite4BtreeNext(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - return rc; - } - *pRes = 0; - if( pPage->leaf ){ - return SQLITE_OK; - } - rc = moveToLeftmost(pCur); - return rc; -} - - -/* -** Step the cursor to the back to the previous entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the first entry in the database before -** this routine was called, then set *pRes=1. -*/ -int sqlite4BtreePrevious(BtCursor *pCur, int *pRes){ - int rc; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - pCur->atLast = 0; - if( CURSOR_INVALID==pCur->eState ){ - *pRes = 1; - return SQLITE_OK; - } - if( pCur->skipNext<0 ){ - pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; - - pPage = pCur->apPage[pCur->iPage]; - assert( pPage->isInit ); - if( !pPage->leaf ){ - int idx = pCur->aiIdx[pCur->iPage]; - rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); - if( rc ){ - return rc; - } - rc = moveToRightmost(pCur); - }else{ - while( pCur->aiIdx[pCur->iPage]==0 ){ - if( pCur->iPage==0 ){ - pCur->eState = CURSOR_INVALID; - *pRes = 1; - return SQLITE_OK; - } - moveToParent(pCur); - } - pCur->info.nSize = 0; - pCur->validNKey = 0; - - pCur->aiIdx[pCur->iPage]--; - pPage = pCur->apPage[pCur->iPage]; - if( pPage->intKey && !pPage->leaf ){ - rc = sqlite4BtreePrevious(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - } - *pRes = 0; - return rc; -} - -/* -** Allocate a new page from the database file. -** -** The new page is marked as dirty. (In other words, sqlite4PagerWrite() -** has already been called on the new page.) The new page has also -** been referenced and the calling routine is responsible for calling -** sqlite4PagerUnref() on the new page when it is done. -** -** SQLITE_OK is returned on success. Any other return value indicates -** an error. *ppPage and *pPgno are undefined in the event of an error. -** Do not invoke sqlite4PagerUnref() on *ppPage if an error is returned. -** -** If the "nearby" parameter is not 0, then a (feeble) effort is made to -** locate a page close to the page number "nearby". This can be used in an -** attempt to keep related pages close to each other in the database file, -** which in turn can make database access faster. -** -** If the "exact" parameter is not 0, and the page-number nearby exists -** anywhere on the free-list, then it is guarenteed to be returned. This -** is only used by auto-vacuum databases when allocating a new table. -*/ -static int allocateBtreePage( - BtShared *pBt, - MemPage **ppPage, - Pgno *pPgno, - Pgno nearby, - u8 exact -){ - MemPage *pPage1; - int rc; - u32 n; /* Number of pages on the freelist */ - u32 k; /* Number of leaves on the trunk of the freelist */ - MemPage *pTrunk = 0; - MemPage *pPrevTrunk = 0; - Pgno mxPage; /* Total size of the database file */ - - assert( sqlite4_mutex_held(pBt->mutex) ); - pPage1 = pBt->pPage1; - mxPage = btreePagecount(pBt); - n = get4byte(&pPage1->aData[36]); - testcase( n==mxPage-1 ); - if( n>=mxPage ){ - return SQLITE_CORRUPT_BKPT; - } - if( n>0 ){ - /* There are pages on the freelist. Reuse one of those pages. */ - Pgno iTrunk; - u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ - - /* If the 'exact' parameter was true and a query of the pointer-map - ** shows that the page 'nearby' is somewhere on the free-list, then - ** the entire-list will be searched for that page. - */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( exact && nearby<=mxPage ){ - u8 eType; - assert( nearby>0 ); - assert( pBt->autoVacuum ); - rc = ptrmapGet(pBt, nearby, &eType, 0); - if( rc ) return rc; - if( eType==PTRMAP_FREEPAGE ){ - searchList = 1; - } - *pPgno = nearby; - } -#endif - - /* Decrement the free-list count by 1. Set iTrunk to the index of the - ** first free-list trunk page. iPrevTrunk is initially 1. - */ - rc = sqlite4PagerWrite(pPage1->pDbPage); - if( rc ) return rc; - put4byte(&pPage1->aData[36], n-1); - - /* The code within this loop is run only once if the 'searchList' variable - ** is not true. Otherwise, it runs once for each trunk-page on the - ** free-list until the page 'nearby' is located. - */ - do { - pPrevTrunk = pTrunk; - if( pPrevTrunk ){ - iTrunk = get4byte(&pPrevTrunk->aData[0]); - }else{ - iTrunk = get4byte(&pPage1->aData[32]); - } - testcase( iTrunk==mxPage ); - if( iTrunk>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); - } - if( rc ){ - pTrunk = 0; - goto end_allocate_page; - } - assert( pTrunk!=0 ); - assert( pTrunk->aData!=0 ); - - k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ - if( k==0 && !searchList ){ - /* The trunk has no leaves and the list is not being searched. - ** So extract the trunk page itself and use it as the newly - ** allocated page */ - assert( pPrevTrunk==0 ); - rc = sqlite4PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - *pPgno = iTrunk; - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - *ppPage = pTrunk; - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); - }else if( k>(u32)(pBt->usableSize/4 - 2) ){ - /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; -#ifndef SQLITE_OMIT_AUTOVACUUM - }else if( searchList && nearby==iTrunk ){ - /* The list is being searched and this trunk page is the page - ** to allocate, regardless of whether it has leaves. - */ - assert( *pPgno==iTrunk ); - *ppPage = pTrunk; - searchList = 0; - rc = sqlite4PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - if( k==0 ){ - if( !pPrevTrunk ){ - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - }else{ - rc = sqlite4PagerWrite(pPrevTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); - } - }else{ - /* The trunk page is required by the caller but it contains - ** pointers to free-list leaves. The first leaf becomes a trunk - ** page in this case. - */ - MemPage *pNewTrunk; - Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); - if( iNewTrunk>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } - testcase( iNewTrunk==mxPage ); - rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - rc = sqlite4PagerWrite(pNewTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pNewTrunk); - goto end_allocate_page; - } - memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4); - put4byte(&pNewTrunk->aData[4], k-1); - memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4); - releasePage(pNewTrunk); - if( !pPrevTrunk ){ - assert( sqlite4PagerIswriteable(pPage1->pDbPage) ); - put4byte(&pPage1->aData[32], iNewTrunk); - }else{ - rc = sqlite4PagerWrite(pPrevTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - put4byte(&pPrevTrunk->aData[0], iNewTrunk); - } - } - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); -#endif - }else if( k>0 ){ - /* Extract a leaf from the trunk */ - u32 closest; - Pgno iPage; - unsigned char *aData = pTrunk->aData; - if( nearby>0 ){ - u32 i; - int dist; - closest = 0; - dist = sqlite4AbsInt32(get4byte(&aData[8]) - nearby); - for(i=1; imxPage ){ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } - testcase( iPage==mxPage ); - if( !searchList || iPage==nearby ){ - int noContent; - *pPgno = iPage; - TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" - ": %d more free pages\n", - *pPgno, closest+1, k, pTrunk->pgno, n-1)); - rc = sqlite4PagerWrite(pTrunk->pDbPage); - if( rc ) goto end_allocate_page; - if( closestpDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - } - searchList = 0; - } - } - releasePage(pPrevTrunk); - pPrevTrunk = 0; - }while( searchList ); - }else{ - /* There are no pages on the freelist, so create a new page at the - ** end of the file */ - rc = sqlite4PagerWrite(pBt->pPage1->pDbPage); - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++; - -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){ - /* If *pPgno refers to a pointer-map page, allocate two new pages - ** at the end of the file instead of one. The first allocated page - ** becomes a new pointer-map page, the second is used by the caller. - */ - MemPage *pPg = 0; - TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); - assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1); - if( rc==SQLITE_OK ){ - rc = sqlite4PagerWrite(pPg->pDbPage); - releasePage(pPg); - } - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; } - } -#endif - put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage); - *pPgno = pBt->nPage; - - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, *pPgno, ppPage, 1); - if( rc ) return rc; - rc = sqlite4PagerWrite((*ppPage)->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); - } - - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - -end_allocate_page: - releasePage(pTrunk); - releasePage(pPrevTrunk); - if( rc==SQLITE_OK ){ - if( sqlite4PagerPageRefcount((*ppPage)->pDbPage)>1 ){ - releasePage(*ppPage); - return SQLITE_CORRUPT_BKPT; - } - (*ppPage)->isInit = 0; - }else{ - *ppPage = 0; - } - assert( rc!=SQLITE_OK || sqlite4PagerIswriteable((*ppPage)->pDbPage) ); - return rc; -} - -/* -** This function is used to add page iPage to the database file free-list. -** It is assumed that the page is not already a part of the free-list. -** -** The value passed as the second argument to this function is optional. -** If the caller happens to have a pointer to the MemPage object -** corresponding to page iPage handy, it may pass it as the second value. -** Otherwise, it may pass NULL. -** -** If a pointer to a MemPage object is passed as the second argument, -** its reference count is not altered by this function. -*/ -static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - MemPage *pTrunk = 0; /* Free-list trunk page */ - Pgno iTrunk = 0; /* Page number of free-list trunk page */ - MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ - MemPage *pPage; /* Page being freed. May be NULL. */ - int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ - - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( iPage>1 ); - assert( !pMemPage || pMemPage->pgno==iPage ); - - if( pMemPage ){ - pPage = pMemPage; - sqlite4PagerRef(pPage->pDbPage); - }else{ - pPage = btreePageLookup(pBt, iPage); - } - - /* Increment the free page count on pPage1 */ - rc = sqlite4PagerWrite(pPage1->pDbPage); - if( rc ) goto freepage_out; - nFree = get4byte(&pPage1->aData[36]); - put4byte(&pPage1->aData[36], nFree+1); - - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - /* If the secure_delete option is enabled, then - ** always fully overwrite deleted information with zeros. - */ - if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) - || ((rc = sqlite4PagerWrite(pPage->pDbPage))!=0) - ){ - goto freepage_out; - } - memset(pPage->aData, 0, pPage->pBt->pageSize); - } - - /* If the database supports auto-vacuum, write an entry in the pointer-map - ** to indicate that the page is free. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); - if( rc ) goto freepage_out; - } - - /* Now manipulate the actual database free-list structure. There are two - ** possibilities. If the free-list is currently empty, or if the first - ** trunk page in the free-list is full, then this page will become a - ** new free-list trunk page. Otherwise, it will become a leaf of the - ** first trunk page in the current free-list. This block tests if it - ** is possible to add the page as a new free-list leaf. - */ - if( nFree!=0 ){ - u32 nLeaf; /* Initial number of leaf cells on trunk page */ - - iTrunk = get4byte(&pPage1->aData[32]); - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } - - nLeaf = get4byte(&pTrunk->aData[4]); - assert( pBt->usableSize>32 ); - if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ - rc = SQLITE_CORRUPT_BKPT; - goto freepage_out; - } - if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ - /* In this case there is room on the trunk page to insert the page - ** being freed as a new leaf. - ** - ** Note that the trunk page is not really full until it contains - ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have - ** coded. But due to a coding error in versions of SQLite prior to - ** 3.6.0, databases with freelist trunk pages holding more than - ** usableSize/4 - 8 entries will be reported as corrupt. In order - ** to maintain backwards compatibility with older versions of SQLite, - ** we will continue to restrict the number of entries to usableSize/4 - 8 - ** for now. At some point in the future (once everyone has upgraded - ** to 3.6.0 or later) we should consider fixing the conditional above - ** to read "usableSize/4-2" instead of "usableSize/4-8". - */ - rc = sqlite4PagerWrite(pTrunk->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pTrunk->aData[4], nLeaf+1); - put4byte(&pTrunk->aData[8+nLeaf*4], iPage); - if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){ - sqlite4PagerDontWrite(pPage->pDbPage); - } - rc = btreeSetHasContent(pBt, iPage); - } - TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); - goto freepage_out; - } - } - - /* If control flows to this point, then it was not possible to add the - ** the page being freed as a leaf page of the first trunk in the free-list. - ** Possibly because the free-list is empty, or possibly because the - ** first trunk in the free-list is full. Either way, the page being freed - ** will become the new first trunk page in the free-list. - */ - if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ - goto freepage_out; - } - rc = sqlite4PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } - put4byte(pPage->aData, iTrunk); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], iPage); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); - -freepage_out: - if( pPage ){ - pPage->isInit = 0; - } - releasePage(pPage); - releasePage(pTrunk); - return rc; -} -static void freePage(MemPage *pPage, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); - } -} - -/* -** Free any overflow pages associated with the given Cell. -*/ -static int clearCell(MemPage *pPage, unsigned char *pCell){ - BtShared *pBt = pPage->pBt; - CellInfo info; - Pgno ovflPgno; - int rc; - int nOvfl; - u32 ovflPageSize; - - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - btreeParseCellPtr(pPage, pCell, &info); - if( info.iOverflow==0 ){ - return SQLITE_OK; /* No overflow pages. Return without doing anything */ - } - if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){ - return SQLITE_CORRUPT; /* Cell extends past end of page */ - } - ovflPgno = get4byte(&pCell[info.iOverflow]); - assert( pBt->usableSize > 4 ); - ovflPageSize = pBt->usableSize - 4; - nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; - assert( ovflPgno==0 || nOvfl>0 ); - while( nOvfl-- ){ - Pgno iNext = 0; - MemPage *pOvfl = 0; - if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){ - /* 0 is not a legal page number and page 1 cannot be an - ** overflow page. Therefore if ovflPgno<2 or past the end of the - ** file the database must be corrupt. */ - return SQLITE_CORRUPT_BKPT; - } - if( nOvfl ){ - rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); - if( rc ) return rc; - } - - if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) ) - && sqlite4PagerPageRefcount(pOvfl->pDbPage)!=1 - ){ - /* There is no reason any cursor should have an outstanding reference - ** to an overflow page belonging to a cell that is being deleted/updated. - ** So if there exists more than one reference to this page, then it - ** must not really be an overflow page and the database must be corrupt. - ** It is helpful to detect this before calling freePage2(), as - ** freePage2() may zero the page contents if secure-delete mode is - ** enabled. If this 'overflow' page happens to be a page that the - ** caller is iterating through or using in some other way, this - ** can be problematic. - */ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = freePage2(pBt, pOvfl, ovflPgno); - } - - if( pOvfl ){ - sqlite4PagerUnref(pOvfl->pDbPage); - } - if( rc ) return rc; - ovflPgno = iNext; - } - return SQLITE_OK; -} - -/* -** Create the byte sequence used to represent a cell on page pPage -** and write that byte sequence into pCell[]. Overflow pages are -** allocated and filled in as necessary. The calling procedure -** is responsible for making sure sufficient space has been allocated -** for pCell[]. -** -** Note that pCell does not necessary need to point to the pPage->aData -** area. pCell might point to some temporary storage. The cell will -** be constructed in this temporary area then copied into pPage->aData -** later. -*/ -static int fillInCell( - MemPage *pPage, /* The page that contains the cell */ - unsigned char *pCell, /* Complete text of the cell */ - const void *pKey, i64 nKey, /* The key */ - const void *pData,int nData, /* The data */ - int nZero, /* Extra zero bytes to append to pData */ - int *pnSize /* Write cell size here */ -){ - int nPayload; - const u8 *pSrc; - int nSrc, n, rc; - int spaceLeft; - MemPage *pOvfl = 0; - MemPage *pToRelease = 0; - unsigned char *pPrior; - unsigned char *pPayload; - BtShared *pBt = pPage->pBt; - Pgno pgnoOvfl = 0; - int nHeader; - CellInfo info; - - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - - /* pPage is not necessarily writeable since pCell might be auxiliary - ** buffer space that is separate from the pPage buffer area */ - assert( pCellaData || pCell>=&pPage->aData[pBt->pageSize] - || sqlite4PagerIswriteable(pPage->pDbPage) ); - - /* Fill in the header. */ - nHeader = 0; - if( !pPage->leaf ){ - nHeader += 4; - } - if( pPage->hasData ){ - nHeader += putVarint(&pCell[nHeader], nData+nZero); - }else{ - nData = nZero = 0; - } - nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - btreeParseCellPtr(pPage, pCell, &info); - assert( info.nHeader==nHeader ); - assert( info.nKey==nKey ); - assert( info.nData==(u32)(nData+nZero) ); - - /* Fill in the payload */ - nPayload = nData + nZero; - if( pPage->intKey ){ - pSrc = pData; - nSrc = nData; - nData = 0; - }else{ - if( NEVER(nKey>0x7fffffff || pKey==0) ){ - return SQLITE_CORRUPT_BKPT; - } - nPayload += (int)nKey; - pSrc = pKey; - nSrc = (int)nKey; - } - *pnSize = info.nSize; - spaceLeft = info.nLocal; - pPayload = &pCell[nHeader]; - pPrior = &pCell[info.iOverflow]; - - while( nPayload>0 ){ - if( spaceLeft==0 ){ -#ifndef SQLITE_OMIT_AUTOVACUUM - Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ - if( pBt->autoVacuum ){ - do{ - pgnoOvfl++; - } while( - PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt) - ); - } -#endif - rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0); -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the database supports auto-vacuum, and the second or subsequent - ** overflow page is being allocated, add an entry to the pointer-map - ** for that page now. - ** - ** If this is the first overflow page, then write a partial entry - ** to the pointer-map. If we write nothing to this pointer-map slot, - ** then the optimistic overflow chain processing in clearCell() - ** may misinterpret the uninitialised values and delete the - ** wrong pages from the database. - */ - if( pBt->autoVacuum && rc==SQLITE_OK ){ - u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); - ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); - if( rc ){ - releasePage(pOvfl); - } - } -#endif - if( rc ){ - releasePage(pToRelease); - return rc; - } - - /* If pToRelease is not zero than pPrior points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlite4PagerIswriteable(pToRelease->pDbPage) ); - - /* If pPrior is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPrioraData || pPrior>=&pPage->aData[pBt->pageSize] - || sqlite4PagerIswriteable(pPage->pDbPage) ); - - put4byte(pPrior, pgnoOvfl); - releasePage(pToRelease); - pToRelease = pOvfl; - pPrior = pOvfl->aData; - put4byte(pPrior, 0); - pPayload = &pOvfl->aData[4]; - spaceLeft = pBt->usableSize - 4; - } - n = nPayload; - if( n>spaceLeft ) n = spaceLeft; - - /* If pToRelease is not zero than pPayload points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlite4PagerIswriteable(pToRelease->pDbPage) ); - - /* If pPayload is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPayloadaData || pPayload>=&pPage->aData[pBt->pageSize] - || sqlite4PagerIswriteable(pPage->pDbPage) ); - - if( nSrc>0 ){ - if( n>nSrc ) n = nSrc; - assert( pSrc ); - memcpy(pPayload, pSrc, n); - }else{ - memset(pPayload, 0, n); - } - nPayload -= n; - pPayload += n; - pSrc += n; - nSrc -= n; - spaceLeft -= n; - if( nSrc==0 ){ - nSrc = nData; - pSrc = pData; - } - } - releasePage(pToRelease); - return SQLITE_OK; -} - -/* -** Remove the i-th cell from pPage. This routine effects pPage only. -** The cell content is not freed or deallocated. It is assumed that -** the cell content has been copied someplace else. This routine just -** removes the reference to the cell from pPage. -** -** "sz" must be the number of bytes in the cell. -*/ -static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ - u32 pc; /* Offset to cell content of cell being deleted */ - u8 *data; /* pPage->aData */ - u8 *ptr; /* Used to move bytes around within data[] */ - u8 *endPtr; /* End of loop */ - int rc; /* The return code */ - int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ - - if( *pRC ) return; - - assert( idx>=0 && idxnCell ); - assert( sz==cellSize(pPage, idx) ); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - data = pPage->aData; - ptr = &pPage->aCellIdx[2*idx]; - pc = get2byte(ptr); - hdr = pPage->hdrOffset; - testcase( pc==get2byte(&data[hdr+5]) ); - testcase( pc+sz==pPage->pBt->usableSize ); - if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - rc = freeSpace(pPage, pc, sz); - if( rc ){ - *pRC = rc; - return; - } - endPtr = &pPage->aCellIdx[2*pPage->nCell - 2]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptrnCell--; - put2byte(&data[hdr+3], pPage->nCell); - pPage->nFree += 2; -} - -/* -** Insert a new cell on pPage at cell index "i". pCell points to the -** content of the cell. -** -** If the cell content will fit on the page, then put it there. If it -** will not fit, then make a copy of the cell content into pTemp if -** pTemp is not null. Regardless of pTemp, allocate a new entry -** in pPage->aOvfl[] and make it point to the cell content (either -** in pTemp or the original pCell) and also record its index. -** Allocating a new entry in pPage->aCell[] implies that -** pPage->nOverflow is incremented. -** -** If nSkip is non-zero, then do not copy the first nSkip bytes of the -** cell. The caller will overwrite them after this function returns. If -** nSkip is non-zero, then pCell may not point to an invalid memory location -** (but pCell+nSkip is always valid). -*/ -static void insertCell( - MemPage *pPage, /* Page into which we are copying */ - int i, /* New cell becomes the i-th cell of the page */ - u8 *pCell, /* Content of the new cell */ - int sz, /* Bytes of content in pCell */ - u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ - int *pRC /* Read and write return code from here */ -){ - int idx = 0; /* Where to write new cell content in data[] */ - int j; /* Loop counter */ - int end; /* First byte past the last cell pointer in data[] */ - int ins; /* Index in data[] where new cell pointer is inserted */ - int cellOffset; /* Address of first cell pointer in data[] */ - u8 *data; /* The content of the whole page */ - u8 *ptr; /* Used for moving information around in data[] */ - u8 *endPtr; /* End of the loop */ - - int nSkip = (iChild ? 4 : 0); - - if( *pRC ) return; - - assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); - assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); - assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - /* The cell should normally be sized correctly. However, when moving a - ** malformed cell from a leaf page to an interior page, if the cell size - ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size - ** might be less than 8 (leaf-size + pointer) on the interior node. Hence - ** the term after the || in the following assert(). */ - assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) ); - if( pPage->nOverflow || sz+2>pPage->nFree ){ - if( pTemp ){ - memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip); - pCell = pTemp; - } - if( iChild ){ - put4byte(pCell, iChild); - } - j = pPage->nOverflow++; - assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) ); - pPage->aOvfl[j].pCell = pCell; - pPage->aOvfl[j].idx = (u16)i; - }else{ - int rc = sqlite4PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - data = pPage->aData; - cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell; - ins = cellOffset + 2*i; - rc = allocateSpace(pPage, sz, &idx); - if( rc ){ *pRC = rc; return; } - /* The allocateSpace() routine guarantees the following two properties - ** if it returns success */ - assert( idx >= end+2 ); - assert( idx+sz <= (int)pPage->pBt->usableSize ); - pPage->nCell++; - pPage->nFree -= (u16)(2 + sz); - memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); - if( iChild ){ - put4byte(&data[idx], iChild); - } - ptr = &data[end]; - endPtr = &data[ins]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptr>endPtr ){ - *(u16*)ptr = *(u16*)&ptr[-2]; - ptr -= 2; - } - put2byte(&data[ins], idx); - put2byte(&data[pPage->hdrOffset+3], pPage->nCell); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pPage->pBt->autoVacuum ){ - /* The cell may contain a pointer to an overflow page. If so, write - ** the entry for the overflow page into the pointer map. - */ - ptrmapPutOvflPtr(pPage, pCell, pRC); - } -#endif - } -} - -/* -** Add a list of cells to a page. The page should be initially empty. -** The cells are guaranteed to fit on the page. -*/ -static void assemblePage( - MemPage *pPage, /* The page to be assemblied */ - int nCell, /* The number of cells to add to this page */ - u8 **apCell, /* Pointers to cell bodies */ - u16 *aSize /* Sizes of the cells */ -){ - int i; /* Loop counter */ - u8 *pCellptr; /* Address of next cell pointer */ - int cellbody; /* Address of next cell body */ - u8 * const data = pPage->aData; /* Pointer to data for pPage */ - const int hdr = pPage->hdrOffset; /* Offset of header on pPage */ - const int nUsable = pPage->pBt->usableSize; /* Usable size of page */ - - assert( pPage->nOverflow==0 ); - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt) - && (int)MX_CELL(pPage->pBt)<=10921); - assert( sqlite4PagerIswriteable(pPage->pDbPage) ); - - /* Check that the page has just been zeroed by zeroPage() */ - assert( pPage->nCell==0 ); - assert( get2byteNotZero(&data[hdr+5])==nUsable ); - - pCellptr = &pPage->aCellIdx[nCell*2]; - cellbody = nUsable; - for(i=nCell-1; i>=0; i--){ - u16 sz = aSize[i]; - pCellptr -= 2; - cellbody -= sz; - put2byte(pCellptr, cellbody); - memcpy(&data[cellbody], apCell[i], sz); - } - put2byte(&data[hdr+3], nCell); - put2byte(&data[hdr+5], cellbody); - pPage->nFree -= (nCell*2 + nUsable - cellbody); - pPage->nCell = (u16)nCell; -} - -/* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB (NN*2+1) /* Total pages involved in the balance */ - - -#ifndef SQLITE_OMIT_QUICKBALANCE -/* -** This version of balance() handles the common special case where -** a new entry is being inserted on the extreme right-end of the -** tree, in other words, when the new entry will become the largest -** entry in the tree. -** -** Instead of trying to balance the 3 right-most leaf pages, just add -** a new page to the right-hand side and put the one new entry in -** that page. This leaves the right side of the tree somewhat -** unbalanced. But odds are that we will be inserting new entries -** at the end soon afterwards so the nearly empty page will quickly -** fill up. On average. -** -** pPage is the leaf page which is the right-most page in the tree. -** pParent is its parent. pPage must have a single overflow entry -** which is also the right-most entry on the page. -** -** The pSpace buffer is used to store a temporary copy of the divider -** cell that will be inserted into pParent. Such a cell consists of a 4 -** byte page number followed by a variable length integer. In other -** words, at most 13 bytes. Hence the pSpace buffer must be at -** least 13 bytes in size. -*/ -static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - BtShared *const pBt = pPage->pBt; /* B-Tree Database */ - MemPage *pNew; /* Newly allocated page */ - int rc; /* Return Code */ - Pgno pgnoNew; /* Page number of pNew */ - - assert( sqlite4_mutex_held(pPage->pBt->mutex) ); - assert( sqlite4PagerIswriteable(pParent->pDbPage) ); - assert( pPage->nOverflow==1 ); - - /* This error condition is now caught prior to reaching this function */ - if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; - - /* Allocate a new page. This page will become the right-sibling of - ** pPage. Make the parent page writable, so that the new divider cell - ** may be inserted. If both these operations are successful, proceed. - */ - rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); - - if( rc==SQLITE_OK ){ - - u8 *pOut = &pSpace[4]; - u8 *pCell = pPage->aOvfl[0].pCell; - u16 szCell = cellSizePtr(pPage, pCell); - u8 *pStop; - - assert( sqlite4PagerIswriteable(pNew->pDbPage) ); - assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); - zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); - assemblePage(pNew, 1, &pCell, &szCell); - - /* If this is an auto-vacuum database, update the pointer map - ** with entries for the new page, and any pointer from the - ** cell on the page to an overflow page. If either of these - ** operations fails, the return code is set, but the contents - ** of the parent page are still manipulated by thh code below. - ** That is Ok, at this point the parent page is guaranteed to - ** be marked as dirty. Returning an error code will cause a - ** rollback, undoing any changes made to the parent page. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); - if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); - } - } - - /* Create a divider cell to insert into pParent. The divider cell - ** consists of a 4-byte page number (the page number of pPage) and - ** a variable length key value (which must be the same value as the - ** largest key on pPage). - ** - ** To find the largest key value on pPage, first find the right-most - ** cell on pPage. The first two fields of this cell are the - ** record-length (a variable length integer at most 32-bits in size) - ** and the key value (a variable length integer, may have any value). - ** The first of the while(...) loops below skips over the record-length - ** field. The second while(...) loop copies the key value from the - ** cell on pPage into the pSpace buffer. - */ - pCell = findCell(pPage, pPage->nCell-1); - pStop = &pCell[9]; - while( (*(pCell++)&0x80) && pCellnCell, pSpace, (int)(pOut-pSpace), - 0, pPage->pgno, &rc); - - /* Set the right-child pointer of pParent to point to the new page. */ - put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); - - /* Release the reference to the new page. */ - releasePage(pNew); - } - - return rc; -} -#endif /* SQLITE_OMIT_QUICKBALANCE */ - -#if 0 -/* -** This function does not contribute anything to the operation of SQLite. -** it is sometimes activated temporarily while debugging code responsible -** for setting pointer-map entries. -*/ -static int ptrmapCheckPages(MemPage **apPage, int nPage){ - int i, j; - for(i=0; ipBt; - assert( pPage->isInit ); - - for(j=0; jnCell; j++){ - CellInfo info; - u8 *z; - - z = findCell(pPage, j); - btreeParseCellPtr(pPage, z, &info); - if( info.iOverflow ){ - Pgno ovfl = get4byte(&z[info.iOverflow]); - ptrmapGet(pBt, ovfl, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 ); - } - if( !pPage->leaf ){ - Pgno child = get4byte(z); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); - } - } - if( !pPage->leaf ){ - Pgno child = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); - } - } - return 1; -} -#endif - -/* -** This function is used to copy the contents of the b-tree node stored -** on page pFrom to page pTo. If page pFrom was not a leaf page, then -** the pointer-map entries for each child page are updated so that the -** parent page stored in the pointer map is page pTo. If pFrom contained -** any cells with overflow page pointers, then the corresponding pointer -** map entries are also updated so that the parent page is page pTo. -** -** If pFrom is currently carrying any overflow cells (entries in the -** MemPage.aOvfl[] array), they are not copied to pTo. -** -** Before returning, page pTo is reinitialized using btreeInitPage(). -** -** The performance of this function is not critical. It is only used by -** the balance_shallower() and balance_deeper() procedures, neither of -** which are called often under normal circumstances. -*/ -static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - BtShared * const pBt = pFrom->pBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc; - int iData; - - - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); - - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); - - /* Reinitialize page pTo so that the contents of the MemPage structure - ** match the new data. The initialization of pTo can actually fail under - ** fairly obscure circumstances, even though it is a copy of initialized - ** page pFrom. - */ - pTo->isInit = 0; - rc = btreeInitPage(pTo); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. - */ - if( ISAUTOVACUUM ){ - *pRC = setChildPtrmaps(pTo); - } - } -} - -/* -** This routine redistributes cells on the iParentIdx'th child of pParent -** (hereafter "the page") and up to 2 siblings so that all pages have about the -** same amount of free space. Usually a single sibling on either side of the -** page are used in the balancing, though both siblings might come from one -** side if the page is the first or last child of its parent. If the page -** has fewer than 2 siblings (something which can only happen if the page -** is a root page or a child of a root page) then all available siblings -** participate in the balancing. -** -** The number of siblings of the page might be increased or decreased by -** one or two in an effort to keep pages nearly full but not over full. -** -** Note that when this routine is called, some of the cells on the page -** might not actually be stored in MemPage.aData[]. This can happen -** if the page is overfull. This routine ensures that all cells allocated -** to the page and its siblings fit into MemPage.aData[] before returning. -** -** In the course of balancing the page and its siblings, cells may be -** inserted into or removed from the parent page (pParent). Doing so -** may cause the parent page to become overfull or underfull. If this -** happens, it is the responsibility of the caller to invoke the correct -** balancing routine to fix this problem (see the balance() routine). -** -** If this routine fails for any reason, it might leave the database -** in a corrupted state. So if this routine fails, the database should -** be rolled back. -** -** The third argument to this function, aOvflSpace, is a pointer to a -** buffer big enough to hold one page. If while inserting cells into the parent -** page (pParent) the parent page becomes overfull, this buffer is -** used to store the parent's overflow cells. Because this function inserts -** a maximum of four divider cells into the parent page, and the maximum -** size of a cell stored within an internal node is always less than 1/4 -** of the page-size, the aOvflSpace[] buffer is guaranteed to be large -** enough for all overflow cells. -** -** If aOvflSpace is set to a null pointer, this function returns -** SQLITE_NOMEM. -*/ -static int balance_nonroot( - MemPage *pParent, /* Parent page of siblings being balanced */ - int iParentIdx, /* Index of "the page" in pParent */ - u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */ - int isRoot /* True if pParent is a root-page */ -){ - BtShared *pBt; /* The whole database */ - int nCell = 0; /* Number of cells in apCell[] */ - int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ - int nNew = 0; /* Number of pages in apNew[] */ - int nOld; /* Number of pages in apOld[] */ - int i, j, k; /* Loop counters */ - int nxDiv; /* Next divider slot in pParent->aCell[] */ - int rc = SQLITE_OK; /* The return code */ - u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */ - int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ - int usableSpace; /* Bytes in pPage beyond the header */ - int pageFlags; /* Value of pPage->aData[0] */ - int subtotal; /* Subtotal of bytes in cells on one page */ - int iSpace1 = 0; /* First unused byte of aSpace1[] */ - int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ - int szScratch; /* Size of scratch memory requested */ - MemPage *apOld[NB]; /* pPage and up to two siblings */ - MemPage *apCopy[NB]; /* Private copies of apOld[] pages */ - MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ - u8 *pRight; /* Location in parent of right-sibling pointer */ - u8 *apDiv[NB-1]; /* Divider cells in pParent */ - int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */ - int szNew[NB+2]; /* Combined size of cells place on i-th page */ - u8 **apCell = 0; /* All cells begin balanced */ - u16 *szCell; /* Local size of all cells in apCell[] */ - u8 *aSpace1; /* Space for copies of dividers cells */ - Pgno pgno; /* Temp var to store a page number in */ - - pBt = pParent->pBt; - assert( sqlite4_mutex_held(pBt->mutex) ); - assert( sqlite4PagerIswriteable(pParent->pDbPage) ); - -#if 0 - TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); -#endif - - /* At this point pParent may have at most one overflow cell. And if - ** this overflow cell is present, it must be the cell with - ** index iParentIdx. This scenario comes about when this function - ** is called (indirectly) from sqlite4BtreeDelete(). - */ - assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); - assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx ); - - if( !aOvflSpace ){ - return SQLITE_NOMEM; - } - - /* Find the sibling pages to balance. Also locate the cells in pParent - ** that divide the siblings. An attempt is made to find NN siblings on - ** either side of pPage. More siblings are taken from one side, however, - ** if there are fewer than NN siblings on the other side. If pParent - ** has NB or fewer children then all children of pParent are taken. - ** - ** This loop also drops the divider cells from the parent page. This - ** way, the remainder of the function does not have to deal with any - ** overflow cells in the parent page, since if any existed they will - ** have already been removed. - */ - i = pParent->nOverflow + pParent->nCell; - if( i<2 ){ - nxDiv = 0; - nOld = i+1; - }else{ - nOld = 3; - if( iParentIdx==0 ){ - nxDiv = 0; - }else if( iParentIdx==i ){ - nxDiv = i-2; - }else{ - nxDiv = iParentIdx-1; - } - i = 2; - } - if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){ - pRight = &pParent->aData[pParent->hdrOffset+8]; - }else{ - pRight = findCell(pParent, i+nxDiv-pParent->nOverflow); - } - pgno = get4byte(pRight); - while( 1 ){ - rc = getAndInitPage(pBt, pgno, &apOld[i]); - if( rc ){ - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; - } - nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; - if( (i--)==0 ) break; - - if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){ - apDiv[i] = pParent->aOvfl[0].pCell; - pgno = get4byte(apDiv[i]); - szNew[i] = cellSizePtr(pParent, apDiv[i]); - pParent->nOverflow = 0; - }else{ - apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow); - pgno = get4byte(apDiv[i]); - szNew[i] = cellSizePtr(pParent, apDiv[i]); - - /* Drop the cell from the parent page. apDiv[i] still points to - ** the cell within the parent, even though it has been dropped. - ** This is safe because dropping a cell only overwrites the first - ** four bytes of it, and this function does not need the first - ** four bytes of the divider cell. So the pointer is safe to use - ** later on. - ** - ** But not if we are in secure-delete mode. In secure-delete mode, - ** the dropCell() routine will overwrite the entire cell with zeroes. - ** In this case, temporarily copy the cell into the aOvflSpace[] - ** buffer. It will be copied out again as soon as the aSpace[] buffer - ** is allocated. */ - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - int iOff; - - iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); - if( (iOff+szNew[i])>(int)pBt->usableSize ){ - rc = SQLITE_CORRUPT_BKPT; - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; - }else{ - memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; - } - } - dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); - } - } - - /* Make nMaxCells a multiple of 4 in order to preserve 8-byte - ** alignment */ - nMaxCells = (nMaxCells + 3)&~3; - - /* - ** Allocate space for memory structures - */ - k = pBt->pageSize + ROUND8(sizeof(MemPage)); - szScratch = - nMaxCells*sizeof(u8*) /* apCell */ - + nMaxCells*sizeof(u16) /* szCell */ - + pBt->pageSize /* aSpace1 */ - + k*nOld; /* Page copies (apCopy) */ - apCell = sqlite4ScratchMalloc( szScratch ); - if( apCell==0 ){ - rc = SQLITE_NOMEM; - goto balance_cleanup; - } - szCell = (u16*)&apCell[nMaxCells]; - aSpace1 = (u8*)&szCell[nMaxCells]; - assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); - - /* - ** Load pointers to all cells on sibling pages and the divider cells - ** into the local apCell[] array. Make copies of the divider cells - ** into space obtained from aSpace1[] and remove the the divider Cells - ** from pParent. - ** - ** If the siblings are on leaf pages, then the child pointers of the - ** divider cells are stripped from the cells before they are copied - ** into aSpace1[]. In this way, all cells in apCell[] are without - ** child pointers. If siblings are not leaves, then all cell in - ** apCell[] include child pointers. Either way, all cells in apCell[] - ** are alike. - ** - ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. - ** leafData: 1 if pPage holds key+data and pParent holds only keys. - */ - leafCorrection = apOld[0]->leaf*4; - leafData = apOld[0]->hasData; - for(i=0; ipageSize + k*i]; - memcpy(pOld, apOld[i], sizeof(MemPage)); - pOld->aData = (void*)&pOld[1]; - memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize); - - limit = pOld->nCell+pOld->nOverflow; - if( pOld->nOverflow>0 ){ - for(j=0; jaData; - u16 maskPage = pOld->maskPage; - u16 cellOffset = pOld->cellOffset; - for(j=0; jmaxLocal+23 ); - assert( iSpace1 <= (int)pBt->pageSize ); - memcpy(pTemp, apDiv[i], sz); - apCell[nCell] = pTemp+leafCorrection; - assert( leafCorrection==0 || leafCorrection==4 ); - szCell[nCell] = szCell[nCell] - leafCorrection; - if( !pOld->leaf ){ - assert( leafCorrection==0 ); - assert( pOld->hdrOffset==0 ); - /* The right pointer of the child page pOld becomes the left - ** pointer of the divider cell */ - memcpy(apCell[nCell], &pOld->aData[8], 4); - }else{ - assert( leafCorrection==4 ); - if( szCell[nCell]<4 ){ - /* Do not allow any cells smaller than 4 bytes. */ - szCell[nCell] = 4; - } - } - nCell++; - } - } - - /* - ** Figure out the number of pages needed to hold all nCell cells. - ** Store this number in "k". Also compute szNew[] which is the total - ** size of all cells on the i-th page and cntNew[] which is the index - ** in apCell[] of the cell that divides page i from page i+1. - ** cntNew[k] should equal nCell. - ** - ** Values computed by this block: - ** - ** k: The total number of sibling pages - ** szNew[i]: Spaced used on the i-th sibling page. - ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to - ** the right of the i-th sibling page. - ** usableSpace: Number of bytes of space available on each sibling. - ** - */ - usableSpace = pBt->usableSize - 12 + leafCorrection; - for(subtotal=k=i=0; i usableSpace ){ - szNew[k] = subtotal - szCell[i]; - cntNew[k] = i; - if( leafData ){ i--; } - subtotal = 0; - k++; - if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } - } - } - szNew[k] = subtotal; - cntNew[k] = nCell; - k++; - - /* - ** The packing computed by the previous block is biased toward the siblings - ** on the left side. The left siblings are always nearly full, while the - ** right-most sibling might be nearly empty. This block of code attempts - ** to adjust the packing of siblings to get a better balance. - ** - ** This adjustment is more than an optimization. The packing above might - ** be so out of balance as to be illegal. For example, the right-most - ** sibling might be completely empty. This adjustment is not optional. - */ - for(i=k-1; i>0; i--){ - int szRight = szNew[i]; /* Size of sibling on the right */ - int szLeft = szNew[i-1]; /* Size of sibling on the left */ - int r; /* Index of right-most cell in left sibling */ - int d; /* Index of first cell to the left of right sibling */ - - r = cntNew[i-1] - 1; - d = r + 1 - leafData; - assert( d0) or pPage is - ** a virtual root page. A virtual root page is when the real root - ** page is page 1 and we are the only child of that page. - ** - ** UPDATE: The assert() below is not necessarily true if the database - ** file is corrupt. The corruption will be detected and reported later - ** in this procedure so there is no need to act upon it now. - */ -#if 0 - assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) ); -#endif - - TRACE(("BALANCE: old: %d %d %d ", - apOld[0]->pgno, - nOld>=2 ? apOld[1]->pgno : 0, - nOld>=3 ? apOld[2]->pgno : 0 - )); - - /* - ** Allocate k new pages. Reuse old pages where possible. - */ - if( apOld[0]->pgno<=1 ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; - } - pageFlags = apOld[0]->aData[0]; - for(i=0; ipDbPage); - nNew++; - if( rc ) goto balance_cleanup; - }else{ - assert( i>0 ); - rc = allocateBtreePage(pBt, &pNew, &pgno, pgno, 0); - if( rc ) goto balance_cleanup; - apNew[i] = pNew; - nNew++; - - /* Set the pointer-map entry for the new sibling page. */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); - if( rc!=SQLITE_OK ){ - goto balance_cleanup; - } - } - } - } - - /* Free any old pages that were not reused as new pages. - */ - while( ipgno; - int minI = i; - for(j=i+1; jpgno<(unsigned)minV ){ - minI = j; - minV = apNew[j]->pgno; - } - } - if( minI>i ){ - MemPage *pT; - pT = apNew[i]; - apNew[i] = apNew[minI]; - apNew[minI] = pT; - } - } - TRACE(("new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n", - apNew[0]->pgno, szNew[0], - nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, - nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0, - nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0, - nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0)); - - assert( sqlite4PagerIswriteable(pParent->pDbPage) ); - put4byte(pRight, apNew[nNew-1]->pgno); - - /* - ** Evenly distribute the data in apCell[] across the new pages. - ** Insert divider cells into pParent as necessary. - */ - j = 0; - for(i=0; inCell>0 || (nNew==1 && cntNew[0]==0) ); - assert( pNew->nOverflow==0 ); - - j = cntNew[i]; - - /* If the sibling page assembled above was not the right-most sibling, - ** insert a divider cell into the parent page. - */ - assert( ileaf ){ - memcpy(&pNew->aData[8], pCell, 4); - }else if( leafData ){ - /* If the tree is a leaf-data tree, and the siblings are leaves, - ** then there is no divider cell in apCell[]. Instead, the divider - ** cell consists of the integer key for the right-most cell of - ** the sibling-page assembled above only. - */ - CellInfo info; - j--; - btreeParseCellPtr(pNew, apCell[j], &info); - pCell = pTemp; - sz = 4 + putVarint(&pCell[4], info.nKey); - pTemp = 0; - }else{ - pCell -= 4; - /* Obscure case for non-leaf-data trees: If the cell at pCell was - ** previously stored on a leaf node, and its reported size was 4 - ** bytes, then it may actually be smaller than this - ** (see btreeParseCellPtr(), 4 bytes is the minimum size of - ** any cell). But it is important to pass the correct size to - ** insertCell(), so reparse the cell now. - ** - ** Note that this can never happen in an SQLite data file, as all - ** cells are at least 4 bytes. It only happens in b-trees used - ** to evaluate "IN (SELECT ...)" and similar clauses. - */ - if( szCell[j]==4 ){ - assert(leafCorrection==4); - sz = cellSizePtr(pParent, pCell); - } - } - iOvflSpace += sz; - assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace <= (int)pBt->pageSize ); - insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); - if( rc!=SQLITE_OK ) goto balance_cleanup; - assert( sqlite4PagerIswriteable(pParent->pDbPage) ); - - j++; - nxDiv++; - } - } - assert( j==nCell ); - assert( nOld>0 ); - assert( nNew>0 ); - if( (pageFlags & PTF_LEAF)==0 ){ - u8 *zChild = &apCopy[nOld-1]->aData[8]; - memcpy(&apNew[nNew-1]->aData[8], zChild, 4); - } - - if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){ - /* The root page of the b-tree now contains no cells. The only sibling - ** page is the right-child of the parent. Copy the contents of the - ** child page into the parent, decreasing the overall height of the - ** b-tree structure by one. This is described as the "balance-shallower" - ** sub-algorithm in some documentation. - ** - ** If this is an auto-vacuum database, the call to copyNodeContent() - ** sets all pointer-map entries corresponding to database image pages - ** for which the pointer is stored within the content being copied. - ** - ** The second assert below verifies that the child page is defragmented - ** (it must be, as it was just reconstructed using assemblePage()). This - ** is important if the parent page happens to be page 1 of the database - ** image. */ - assert( nNew==1 ); - assert( apNew[0]->nFree == - (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) - ); - copyNodeContent(apNew[0], pParent, &rc); - freePage(apNew[0], &rc); - }else if( ISAUTOVACUUM ){ - /* Fix the pointer-map entries for all the cells that were shifted around. - ** There are several different types of pointer-map entries that need to - ** be dealt with by this routine. Some of these have been set already, but - ** many have not. The following is a summary: - ** - ** 1) The entries associated with new sibling pages that were not - ** siblings when this function was called. These have already - ** been set. We don't need to worry about old siblings that were - ** moved to the free-list - the freePage() code has taken care - ** of those. - ** - ** 2) The pointer-map entries associated with the first overflow - ** page in any overflow chains used by new divider cells. These - ** have also already been taken care of by the insertCell() code. - ** - ** 3) If the sibling pages are not leaves, then the child pages of - ** cells stored on the sibling pages may need to be updated. - ** - ** 4) If the sibling pages are not internal intkey nodes, then any - ** overflow pages used by these cells may need to be updated - ** (internal intkey nodes never contain pointers to overflow pages). - ** - ** 5) If the sibling pages are not leaves, then the pointer-map - ** entries for the right-child pages of each sibling may need - ** to be updated. - ** - ** Cases 1 and 2 are dealt with above by other code. The next - ** block deals with cases 3 and 4 and the one after that, case 5. Since - ** setting a pointer map entry is a relatively expensive operation, this - ** code only sets pointer map entries for child or overflow pages that have - ** actually moved between pages. */ - MemPage *pNew = apNew[0]; - MemPage *pOld = apCopy[0]; - int nOverflow = pOld->nOverflow; - int iNextOld = pOld->nCell + nOverflow; - int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); - j = 0; /* Current 'old' sibling page */ - k = 0; /* Current 'new' sibling page */ - for(i=0; inCell + pOld->nOverflow; - if( pOld->nOverflow ){ - nOverflow = pOld->nOverflow; - iOverflow = i + !leafData + pOld->aOvfl[0].idx; - } - isDivider = !leafData; - } - - assert(nOverflow>0 || iOverflowaOvfl[0].idx==pOld->aOvfl[1].idx-1); - assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1); - if( i==iOverflow ){ - isDivider = 1; - if( (--nOverflow)>0 ){ - iOverflow++; - } - } - - if( i==cntNew[k] ){ - /* Cell i is the cell immediately following the last cell on new - ** sibling page k. If the siblings are not leaf pages of an - ** intkey b-tree, then cell i is a divider cell. */ - pNew = apNew[++k]; - if( !leafData ) continue; - } - assert( jpgno!=pNew->pgno ){ - if( !leafCorrection ){ - ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno, &rc); - } - if( szCell[i]>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, apCell[i], &rc); - } - } - } - - if( !leafCorrection ){ - for(i=0; iaData[8]); - ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); - } - } - -#if 0 - /* The ptrmapCheckPages() contains assert() statements that verify that - ** all pointer map pages are set correctly. This is helpful while - ** debugging. This is usually disabled because a corrupt database may - ** cause an assert() statement to fail. */ - ptrmapCheckPages(apNew, nNew); - ptrmapCheckPages(&pParent, 1); -#endif - } - - assert( pParent->isInit ); - TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", - nOld, nNew, nCell)); - - /* - ** Cleanup before returning. - */ -balance_cleanup: - sqlite4ScratchFree(apCell); - for(i=0; ipBt; /* The BTree */ - - assert( pRoot->nOverflow>0 ); - assert( sqlite4_mutex_held(pBt->mutex) ); - - /* Make pRoot, the root page of the b-tree, writable. Allocate a new - ** page that will become the new right-child of pPage. Copy the contents - ** of the node stored on pRoot into the new child page. - */ - rc = sqlite4PagerWrite(pRoot->pDbPage); - if( rc==SQLITE_OK ){ - rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); - copyNodeContent(pRoot, pChild, &rc); - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); - } - } - if( rc ){ - *ppChild = 0; - releasePage(pChild); - return rc; - } - assert( sqlite4PagerIswriteable(pChild->pDbPage) ); - assert( sqlite4PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell ); - - TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); - - /* Copy the overflow cells from pRoot to pChild */ - memcpy(pChild->aOvfl, pRoot->aOvfl, pRoot->nOverflow*sizeof(pRoot->aOvfl[0])); - pChild->nOverflow = pRoot->nOverflow; - - /* Zero the contents of pRoot. Then install pChild as the right-child. */ - zeroPage(pRoot, pChild->aData[0] & ~PTF_LEAF); - put4byte(&pRoot->aData[pRoot->hdrOffset+8], pgnoChild); - - *ppChild = pChild; - return SQLITE_OK; -} - -/* -** The page that pCur currently points to has just been modified in -** some way. This function figures out if this modification means the -** tree needs to be balanced, and if so calls the appropriate balancing -** routine. Balancing routines are: -** -** balance_quick() -** balance_deeper() -** balance_nonroot() -*/ -static int balance(BtCursor *pCur){ - int rc = SQLITE_OK; - const int nMin = pCur->pBt->usableSize * 2 / 3; - u8 aBalanceQuickSpace[13]; - u8 *pFree = 0; - - TESTONLY( int balance_quick_called = 0 ); - TESTONLY( int balance_deeper_called = 0 ); - - do { - int iPage = pCur->iPage; - MemPage *pPage = pCur->apPage[iPage]; - - if( iPage==0 ){ - if( pPage->nOverflow ){ - /* The root page of the b-tree is overfull. In this case call the - ** balance_deeper() function to create a new child for the root-page - ** and copy the current contents of the root-page to it. The - ** next iteration of the do-loop will balance the child page. - */ - assert( (balance_deeper_called++)==0 ); - rc = balance_deeper(pPage, &pCur->apPage[1]); - if( rc==SQLITE_OK ){ - pCur->iPage = 1; - pCur->aiIdx[0] = 0; - pCur->aiIdx[1] = 0; - assert( pCur->apPage[1]->nOverflow ); - } - }else{ - break; - } - }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ - break; - }else{ - MemPage * const pParent = pCur->apPage[iPage-1]; - int const iIdx = pCur->aiIdx[iPage-1]; - - rc = sqlite4PagerWrite(pParent->pDbPage); - if( rc==SQLITE_OK ){ -#ifndef SQLITE_OMIT_QUICKBALANCE - if( pPage->hasData - && pPage->nOverflow==1 - && pPage->aOvfl[0].idx==pPage->nCell - && pParent->pgno!=1 - && pParent->nCell==iIdx - ){ - /* Call balance_quick() to create a new sibling of pPage on which - ** to store the overflow cell. balance_quick() inserts a new cell - ** into pParent, which may cause pParent overflow. If this - ** happens, the next interation of the do-loop will balance pParent - ** use either balance_nonroot() or balance_deeper(). Until this - ** happens, the overflow cell is stored in the aBalanceQuickSpace[] - ** buffer. - ** - ** The purpose of the following assert() is to check that only a - ** single call to balance_quick() is made for each call to this - ** function. If this were not verified, a subtle bug involving reuse - ** of the aBalanceQuickSpace[] might sneak in. - */ - assert( (balance_quick_called++)==0 ); - rc = balance_quick(pParent, pPage, aBalanceQuickSpace); - }else -#endif - { - /* In this case, call balance_nonroot() to redistribute cells - ** between pPage and up to 2 of its sibling pages. This involves - ** modifying the contents of pParent, which may cause pParent to - ** become overfull or underfull. The next iteration of the do-loop - ** will balance the parent page to correct this. - ** - ** If the parent page becomes overfull, the overflow cell or cells - ** are stored in the pSpace buffer allocated immediately below. - ** A subsequent iteration of the do-loop will deal with this by - ** calling balance_nonroot() (balance_deeper() may be called first, - ** but it doesn't deal with overflow cells - just moves them to a - ** different page). Once this subsequent call to balance_nonroot() - ** has completed, it is safe to release the pSpace buffer used by - ** the previous call, as the overflow cell data will have been - ** copied either into the body of a database page or into the new - ** pSpace buffer passed to the latter call to balance_nonroot(). - */ - u8 *pSpace = sqlite4PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1); - if( pFree ){ - /* If pFree is not NULL, it points to the pSpace buffer used - ** by a previous call to balance_nonroot(). Its contents are - ** now stored either on real database pages or within the - ** new pSpace buffer, so it may be safely freed here. */ - sqlite4PageFree(pFree); - } - - /* The pSpace buffer will be freed after the next call to - ** balance_nonroot(), or just before this function returns, whichever - ** comes first. */ - pFree = pSpace; - } - } - - pPage->nOverflow = 0; - - /* The next iteration of the do-loop balances the parent page. */ - releasePage(pPage); - pCur->iPage--; - } - }while( rc==SQLITE_OK ); - - if( pFree ){ - sqlite4PageFree(pFree); - } - return rc; -} - - -/* -** Insert a new record into the BTree. The key is given by (pKey,nKey) -** and the data is given by (pData,nData). The cursor is used only to -** define what table the record should be inserted into. The cursor -** is left pointing at a random location. -** -** For an INTKEY table, only the nKey value of the key is used. pKey is -** ignored. For a ZERODATA table, the pData and nData are both ignored. -** -** If the seekResult parameter is non-zero, then a successful call to -** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already -** been performed. seekResult is the search result returned (a negative -** number if pCur points at an entry that is smaller than (pKey, nKey), or -** a positive value if pCur points at an etry that is larger than -** (pKey, nKey)). -** -** If the seekResult parameter is non-zero, then the caller guarantees that -** cursor pCur is pointing at the existing copy of a row that is to be -** overwritten. If the seekResult parameter is 0, then cursor pCur may -** point to any entry or to no entry at all and so this function has to seek -** the cursor before the new key can be inserted. -*/ -int sqlite4BtreeInsert( - BtCursor *pCur, /* Insert data into the table of this cursor */ - const void *pKey, i64 nKey, /* The key of the new record */ - const void *pData, int nData, /* The data of the new record */ - int nZero, /* Number of extra 0 bytes to append to data */ - int appendBias, /* True if this is likely an append */ - int seekResult /* Result of prior MovetoUnpacked() call */ -){ - int rc; - int loc = seekResult; /* -1: before desired location +1: after */ - int szNew = 0; - int idx; - MemPage *pPage; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - unsigned char *oldCell; - unsigned char *newCell = 0; - - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE - && (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - - /* Assert that the caller has been consistent. If this cursor was opened - ** expecting an index b-tree, then the caller should be inserting blob - ** keys with no associated data. If the cursor was opened expecting an - ** intkey table, the caller should be inserting integer keys with a - ** blob of associated data. */ - assert( (pKey==0)==(pCur->pKeyInfo==0) ); - - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced (assuming this is a replace - ** operation - if it is not, the following is a no-op). */ - if( pCur->pKeyInfo==0 ){ - invalidateIncrblobCursors(p, nKey, 0); - } - - /* Save the positions of any other cursors open on this table. - ** - ** In some cases, the call to btreeMoveto() below is a no-op. For - ** example, when inserting data into a table with auto-generated integer - ** keys, the VDBE layer invokes sqlite4BtreeLast() to figure out the - ** integer key to use. It then calls this function to actually insert the - ** data into the intkey B-Tree. In this case btreeMoveto() recognizes - ** that the cursor is already where it needs to be and returns without - ** doing any work. To avoid thwarting these optimizations, it is important - ** not to clear the cursor here. - */ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; - if( !loc ){ - rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); - if( rc ) return rc; - } - assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); - - pPage = pCur->apPage[pCur->iPage]; - assert( pPage->intKey || nKey>=0 ); - assert( pPage->leaf || !pPage->intKey ); - - TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", - pCur->pgnoRoot, nKey, nData, pPage->pgno, - loc==0 ? "overwrite" : "new entry")); - assert( pPage->isInit ); - allocateTempSpace(pBt); - newCell = pBt->pTmpSpace; - if( newCell==0 ) return SQLITE_NOMEM; - rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); - if( rc ) goto end_insert; - assert( szNew==cellSizePtr(pPage, newCell) ); - assert( szNew <= MX_CELL_SIZE(pBt) ); - idx = pCur->aiIdx[pCur->iPage]; - if( loc==0 ){ - u16 szOld; - assert( idxnCell ); - rc = sqlite4PagerWrite(pPage->pDbPage); - if( rc ){ - goto end_insert; - } - oldCell = findCell(pPage, idx); - if( !pPage->leaf ){ - memcpy(newCell, oldCell, 4); - } - szOld = cellSizePtr(pPage, oldCell); - rc = clearCell(pPage, oldCell); - dropCell(pPage, idx, szOld, &rc); - if( rc ) goto end_insert; - }else if( loc<0 && pPage->nCell>0 ){ - assert( pPage->leaf ); - idx = ++pCur->aiIdx[pCur->iPage]; - }else{ - assert( pPage->leaf ); - } - insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); - assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - - /* If no error has occured and pPage has an overflow cell, call balance() - ** to redistribute the cells within the tree. Since balance() may move - ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey - ** variables. - ** - ** Previous versions of SQLite called moveToRoot() to move the cursor - ** back to the root page as balance() used to invalidate the contents - ** of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that, - ** set the cursor state to "invalid". This makes common insert operations - ** slightly faster. - ** - ** There is a subtle but important optimization here too. When inserting - ** multiple records into an intkey b-tree using a single cursor (as can - ** happen while processing an "INSERT INTO ... SELECT" statement), it - ** is advantageous to leave the cursor pointing to the last entry in - ** the b-tree if possible. If the cursor is left pointing to the last - ** entry in the table, and the next row inserted has an integer key - ** larger than the largest existing key, it is possible to insert the - ** row without seeking the cursor. This can be a big performance boost. - */ - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( rc==SQLITE_OK && pPage->nOverflow ){ - rc = balance(pCur); - - /* Must make sure nOverflow is reset to zero even if the balance() - ** fails. Internal data structure corruption will result otherwise. - ** Also, set the cursor state to invalid. This stops saveCursorPosition() - ** from trying to save the current position of the cursor. */ - pCur->apPage[pCur->iPage]->nOverflow = 0; - pCur->eState = CURSOR_INVALID; - } - assert( pCur->apPage[pCur->iPage]->nOverflow==0 ); - -end_insert: - return rc; -} - -/* -** Delete the entry that the cursor is pointing to. The cursor -** is left pointing at a arbitrary location. -*/ -int sqlite4BtreeDelete(BtCursor *pCur){ - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - int rc; /* Return code */ - MemPage *pPage; /* Page to delete cell from */ - unsigned char *pCell; /* Pointer to cell to delete */ - int iCellIdx; /* Index of cell to delete */ - int iCellDepth; /* Depth of node containing pCell */ - - assert( cursorHoldsMutex(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( pCur->wrFlag ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - - if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) - || NEVER(pCur->eState!=CURSOR_VALID) - ){ - return SQLITE_ERROR; /* Something has gone awry. */ - } - - /* If this is a delete operation to remove a row from a table b-tree, - ** invalidate any incrblob cursors open on the row being deleted. */ - if( pCur->pKeyInfo==0 ){ - invalidateIncrblobCursors(p, pCur->info.nKey, 0); - } - - iCellDepth = pCur->iPage; - iCellIdx = pCur->aiIdx[iCellDepth]; - pPage = pCur->apPage[iCellDepth]; - pCell = findCell(pPage, iCellIdx); - - /* If the page containing the entry to delete is not a leaf page, move - ** the cursor to the largest entry in the tree that is smaller than - ** the entry being deleted. This cell will replace the cell being deleted - ** from the internal node. The 'previous' entry is used for this instead - ** of the 'next' entry, as the previous entry is always a part of the - ** sub-tree headed by the child page of the cell being deleted. This makes - ** balancing the tree following the delete operation easier. */ - if( !pPage->leaf ){ - int notUsed; - rc = sqlite4BtreePrevious(pCur, ¬Used); - if( rc ) return rc; - } - - /* Save the positions of any other cursors open on this table before - ** making any modifications. Make the page containing the entry to be - ** deleted writable. Then free any overflow pages associated with the - ** entry and finally remove the cell itself from within the page. - */ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; - rc = sqlite4PagerWrite(pPage->pDbPage); - if( rc ) return rc; - rc = clearCell(pPage, pCell); - dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc); - if( rc ) return rc; - - /* If the cell deleted was not located on a leaf page, then the cursor - ** is currently pointing to the largest entry in the sub-tree headed - ** by the child-page of the cell that was just deleted from an internal - ** node. The cell from the leaf node needs to be moved to the internal - ** node to replace the deleted cell. */ - if( !pPage->leaf ){ - MemPage *pLeaf = pCur->apPage[pCur->iPage]; - int nCell; - Pgno n = pCur->apPage[iCellDepth+1]->pgno; - unsigned char *pTmp; - - pCell = findCell(pLeaf, pLeaf->nCell-1); - nCell = cellSizePtr(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt) >= nCell ); - - allocateTempSpace(pBt); - pTmp = pBt->pTmpSpace; - - rc = sqlite4PagerWrite(pLeaf->pDbPage); - insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); - dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); - if( rc ) return rc; - } - - /* Balance the tree. If the entry deleted was located on a leaf page, - ** then the cursor still points to that page. In this case the first - ** call to balance() repairs the tree, and the if(...) condition is - ** never true. - ** - ** Otherwise, if the entry deleted was on an internal node page, then - ** pCur is pointing to the leaf page from which a cell was removed to - ** replace the cell deleted from the internal node. This is slightly - ** tricky as the leaf node may be underfull, and the internal node may - ** be either under or overfull. In this case run the balancing algorithm - ** on the leaf node first. If the balance proceeds far enough up the - ** tree that we can be sure that any problem in the internal node has - ** been corrected, so be it. Otherwise, after balancing the leaf node, - ** walk the cursor up the tree to the internal node and balance it as - ** well. */ - rc = balance(pCur); - if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ - while( pCur->iPage>iCellDepth ){ - releasePage(pCur->apPage[pCur->iPage--]); - } - rc = balance(pCur); - } - - if( rc==SQLITE_OK ){ - moveToRoot(pCur); - } - return rc; -} - -/* -** Create a new BTree table. Write into *piTable the page -** number for the root page of the new table. -** -** The type of type is determined by the flags parameter. Only the -** following values of flags are currently in use. Other values for -** flags might not work: -** -** 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 createTabFlags){ - BtShared *pBt = p->pBt; - MemPage *pRoot; - Pgno pgnoRoot; - int rc; - int ptfFlags; /* Page-type flage for the root page of new table */ - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - -#ifdef SQLITE_OMIT_AUTOVACUUM - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ){ - return rc; - } -#else - if( pBt->autoVacuum ){ - Pgno pgnoMove; /* Move a page here to make room for the root-page */ - MemPage *pPageMove; /* The page to move to. */ - - /* Creating a new table may probably require moving an existing database - ** to make room for the new tables root page. In case this page turns - ** out to be an overflow page, delete all overflow page-map caches - ** held by open cursors. - */ - invalidateAllOverflowCache(pBt); - - /* Read the value of meta[3] from the database to determine where the - ** root page of the new table should go. meta[3] is the largest root-page - ** created so far, so the new root-page is (meta[3]+1). - */ - sqlite4BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - pgnoRoot++; - - /* The new root-page may not be allocated on a pointer-map page, or the - ** PENDING_BYTE page. - */ - while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || - pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ - pgnoRoot++; - } - assert( pgnoRoot>=3 ); - - /* Allocate a page. The page that currently resides at pgnoRoot will - ** be moved to the allocated page (unless the allocated page happens - ** to reside at pgnoRoot). - */ - rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1); - if( rc!=SQLITE_OK ){ - return rc; - } - - if( pgnoMove!=pgnoRoot ){ - /* pgnoRoot is the page that will be used for the root-page of - ** the new table (assuming an error did not occur). But we were - ** allocated pgnoMove. If required (i.e. if it was not allocated - ** by extending the file), the current page at position pgnoMove - ** is already journaled. - */ - u8 eType = 0; - Pgno iPtrPage = 0; - - releasePage(pPageMove); - - /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); - if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_BKPT; - } - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } - assert( eType!=PTRMAP_ROOTPAGE ); - assert( eType!=PTRMAP_FREEPAGE ); - rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); - releasePage(pRoot); - - /* Obtain the page at pgnoRoot */ - if( rc!=SQLITE_OK ){ - return rc; - } - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite4PagerWrite(pRoot->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } - }else{ - pRoot = pPageMove; - } - - /* Update the pointer-map and meta-data with the new root-page number. */ - ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); - if( rc ){ - releasePage(pRoot); - return rc; - } - - /* When the new root page was allocated, page 1 was made writable in - ** order either to increase the database filesize, or to decrement the - ** freelist count. Hence, the sqlite4BtreeUpdateMeta() call cannot fail. - */ - assert( sqlite4PagerIswriteable(pBt->pPage1->pDbPage) ); - rc = sqlite4BtreeUpdateMeta(p, 4, pgnoRoot); - if( NEVER(rc) ){ - releasePage(pRoot); - return rc; - } - - }else{ - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ) return rc; - } -#endif - assert( sqlite4PagerIswriteable(pRoot->pDbPage) ); - if( createTabFlags & BTREE_INTKEY ){ - ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; - }else{ - ptfFlags = PTF_ZERODATA | PTF_LEAF; - } - zeroPage(pRoot, ptfFlags); - sqlite4PagerUnref(pRoot->pDbPage); - assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); - *piTable = (int)pgnoRoot; - return SQLITE_OK; -} -int sqlite4BtreeCreateTable(Btree *p, int *piTable, int flags){ - int rc; - sqlite4BtreeEnter(p); - rc = btreeCreateTable(p, piTable, flags); - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Erase the given database page and all its children. Return -** the page to the freelist. -*/ -static int clearDatabasePage( - BtShared *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - int freePageFlag, /* Deallocate page if true */ - int *pnChange /* Add number of Cells freed to this counter */ -){ - MemPage *pPage; - int rc; - unsigned char *pCell; - int i; - - assert( sqlite4_mutex_held(pBt->mutex) ); - if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; - } - - rc = getAndInitPage(pBt, pgno, &pPage); - if( rc ) return rc; - for(i=0; inCell; i++){ - pCell = findCell(pPage, i); - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - } - rc = clearCell(pPage, pCell); - if( rc ) goto cleardatabasepage_out; - } - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - }else if( pnChange ){ - assert( pPage->intKey ); - *pnChange += pPage->nCell; - } - if( freePageFlag ){ - freePage(pPage, &rc); - }else if( (rc = sqlite4PagerWrite(pPage->pDbPage))==0 ){ - zeroPage(pPage, pPage->aData[0] | PTF_LEAF); - } - -cleardatabasepage_out: - releasePage(pPage); - return rc; -} - -/* -** Delete all information from a single table in the database. iTable is -** the page number of the root of the table. After this routine returns, -** the root page is empty, but still exists. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** read cursors on the table. Open write cursors are moved to the -** root of the table. -** -** If pnChange is not NULL, then table iTable must be an intkey table. The -** integer value pointed to by pnChange is incremented by the number of -** entries in the table. -*/ -int sqlite4BtreeClearTable(Btree *p, int iTable, int *pnChange){ - int rc; - BtShared *pBt = p->pBt; - sqlite4BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - - /* Invalidate all incrblob cursors open on table iTable (assuming iTable - ** is the root of a table b-tree - if it is not, the following call is - ** a no-op). */ - invalidateIncrblobCursors(p, 0, 1); - - rc = saveAllCursors(pBt, (Pgno)iTable, 0); - if( SQLITE_OK==rc ){ - rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); - } - sqlite4BtreeLeave(p); - return rc; -} - -/* -** Erase all information in a table and add the root of the table to -** the freelist. Except, the root of the principle table (the one on -** page 1) is never added to the freelist. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** cursors on the table. -** -** If AUTOVACUUM is enabled and the page at iTable is not the last -** root page in the database file, then the last root page -** in the database file is moved into the slot formerly occupied by -** iTable and that last slot formerly occupied by the last root page -** is added to the freelist instead of iTable. In this say, all -** root pages are kept at the beginning of the database file, which -** is necessary for AUTOVACUUM to work right. *piMoved is set to the -** page number that used to be the last root page in the file before -** the move. If no page gets moved, *piMoved is set to 0. -** The last root page is recorded in meta[3] and the value of -** meta[3] is updated by this procedure. -*/ -static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ - int rc; - MemPage *pPage = 0; - BtShared *pBt = p->pBt; - - assert( sqlite4BtreeHoldsMutex(p) ); - assert( p->inTrans==TRANS_WRITE ); - - /* It is illegal to drop a table if any cursors are open on the - ** database. This is because in auto-vacuum mode the backend may - ** need to move another root-page to fill a gap left by the deleted - ** root page. If an open cursor was using this page a problem would - ** occur. - ** - ** This error is caught long before control reaches this point. - */ - if( NEVER(pBt->pCursor) ){ - sqlite4ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); - return SQLITE_LOCKED_SHAREDCACHE; - } - - rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); - if( rc ) return rc; - rc = sqlite4BtreeClearTable(p, iTable, 0); - if( rc ){ - releasePage(pPage); - return rc; - } - - *piMoved = 0; - - if( iTable>1 ){ -#ifdef SQLITE_OMIT_AUTOVACUUM - freePage(pPage, &rc); - releasePage(pPage); -#else - if( pBt->autoVacuum ){ - Pgno maxRootPgno; - sqlite4BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - - if( iTable==maxRootPgno ){ - /* If the table being dropped is the table with the largest root-page - ** number in the database, put the root page on the free list. - */ - freePage(pPage, &rc); - releasePage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - /* The table being dropped does not have the largest root-page - ** number in the database. So move the page that does into the - ** gap left by the deleted root-page. - */ - MemPage *pMove; - releasePage(pPage); - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - pMove = 0; - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - freePage(pMove, &rc); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - *piMoved = maxRootPgno; - } - - /* Set the new 'max-root-page' value in the database header. This - ** is the old value less one, less one more if that happens to - ** be a root-page number, less one again if that is the - ** PENDING_BYTE_PAGE. - */ - maxRootPgno--; - while( maxRootPgno==PENDING_BYTE_PAGE(pBt) - || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ - maxRootPgno--; - } - assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); - - rc = sqlite4BtreeUpdateMeta(p, 4, maxRootPgno); - }else{ - freePage(pPage, &rc); - releasePage(pPage); - } -#endif - }else{ - /* If sqlite4BtreeDropTable was called on page 1. - ** This really never should happen except in a corrupt - ** database. - */ - zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); - releasePage(pPage); - } - return rc; -} -int sqlite4BtreeDropTable(Btree *p, int iTable, int *piMoved){ - int rc; - sqlite4BtreeEnter(p); - rc = btreeDropTable(p, iTable, piMoved); - sqlite4BtreeLeave(p); - return rc; -} - - -/* -** This function may only be called if the b-tree connection already -** has a read or write transaction open on the database. -** -** Read the meta-information out of a database file. Meta[0] -** is the number of free pages currently in the database. Meta[1] -** through meta[15] are available for use by higher layers. Meta[0] -** is read-only, the others are read/write. -** -** The schema layer numbers meta values differently. At the schema -** layer (and the SetCookie and ReadCookie opcodes) the number of -** free pages is not visible. So Cookie[0] is the same as Meta[1]. -*/ -void sqlite4BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - BtShared *pBt = p->pBt; - - sqlite4BtreeEnter(p); - assert( p->inTrans>TRANS_NONE ); - assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); - assert( pBt->pPage1 ); - assert( idx>=0 && idx<=15 ); - - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); - - /* If auto-vacuum is disabled in this build and this is an auto-vacuum - ** database, mark the database as read-only. */ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } -#endif - - sqlite4BtreeLeave(p); -} - -/* -** Write meta-information back into the database. Meta[0] is -** read-only and may not be written. -*/ -int sqlite4BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ - BtShared *pBt = p->pBt; - unsigned char *pP1; - int rc; - assert( idx>=1 && idx<=15 ); - sqlite4BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1!=0 ); - pP1 = pBt->pPage1->aData; - rc = sqlite4PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pP1[36 + idx*4], iMeta); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_INCR_VACUUM ){ - assert( pBt->autoVacuum || iMeta==0 ); - assert( iMeta==0 || iMeta==1 ); - pBt->incrVacuum = (u8)iMeta; - } -#endif - } - sqlite4BtreeLeave(p); - return rc; -} - -#ifndef SQLITE_OMIT_BTREECOUNT -/* -** The first argument, pCur, is a cursor opened on some b-tree. Count the -** number of entries in the b-tree and write the result to *pnEntry. -** -** SQLITE_OK is returned if the operation is successfully executed. -** Otherwise, if an error is encountered (i.e. an IO error or database -** corruption) an SQLite error code is returned. -*/ -int sqlite4BtreeCount(BtCursor *pCur, i64 *pnEntry){ - i64 nEntry = 0; /* Value to return in *pnEntry */ - int rc; /* Return code */ - - if( pCur->pgnoRoot==0 ){ - *pnEntry = 0; - return SQLITE_OK; - } - rc = moveToRoot(pCur); - - /* Unless an error occurs, the following loop runs one iteration for each - ** page in the B-Tree structure (not including overflow pages). - */ - while( rc==SQLITE_OK ){ - int iIdx; /* Index of child node in parent */ - MemPage *pPage; /* Current page of the b-tree */ - - /* If this is a leaf page or the tree is not an int-key tree, then - ** this page contains countable entries. Increment the entry counter - ** accordingly. - */ - pPage = pCur->apPage[pCur->iPage]; - if( pPage->leaf || !pPage->intKey ){ - nEntry += pPage->nCell; - } - - /* pPage is a leaf node. This loop navigates the cursor so that it - ** points to the first interior cell that it points to the parent of - ** the next page in the tree that has not yet been visited. The - ** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell - ** of the page, or to the number of cells in the page if the next page - ** to visit is the right-child of its parent. - ** - ** If all pages in the tree have been visited, return SQLITE_OK to the - ** caller. - */ - if( pPage->leaf ){ - do { - if( pCur->iPage==0 ){ - /* All pages of the b-tree have been visited. Return successfully. */ - *pnEntry = nEntry; - return SQLITE_OK; - } - moveToParent(pCur); - }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); - - pCur->aiIdx[pCur->iPage]++; - pPage = pCur->apPage[pCur->iPage]; - } - - /* Descend to the child node of the cell that the cursor currently - ** points at. This is the right-child if (iIdx==pPage->nCell). - */ - iIdx = pCur->aiIdx[pCur->iPage]; - if( iIdx==pPage->nCell ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - }else{ - rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); - } - } - - /* An error has occurred. Return an error code. */ - return rc; -} -#endif - -/* -** Return the pager associated with a BTree. This routine is used for -** testing and debugging only. -*/ -Pager *sqlite4BtreePager(Btree *p){ - return p->pBt->pPager; -} - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Append a message to the error message string. -*/ -static void checkAppendMsg( - IntegrityCk *pCheck, - char *zMsg1, - const char *zFormat, - ... -){ - va_list ap; - if( !pCheck->mxErr ) return; - pCheck->mxErr--; - pCheck->nErr++; - va_start(ap, zFormat); - if( pCheck->errMsg.nChar ){ - sqlite4StrAccumAppend(&pCheck->errMsg, "\n", 1); - } - if( zMsg1 ){ - sqlite4StrAccumAppend(&pCheck->errMsg, zMsg1, -1); - } - sqlite4VXPrintf(&pCheck->errMsg, 1, zFormat, ap); - va_end(ap); - if( pCheck->errMsg.mallocFailed ){ - pCheck->mallocFailed = 1; - } -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Add 1 to the reference count for page iPage. If this is the second -** reference to the page, add an error message to pCheck->zErrMsg. -** Return 1 if there are 2 ore more references to the page and 0 if -** if this is the first reference to the page. -** -** Also check that the page number is in bounds. -*/ -static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){ - if( iPage==0 ) return 1; - if( iPage>pCheck->nPage ){ - checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); - return 1; - } - if( pCheck->anRef[iPage]==1 ){ - checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); - return 1; - } - return (pCheck->anRef[iPage]++)>1; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Check that the entry in the pointer-map for page iChild maps to -** page iParent, pointer type ptrType. If not, append an error message -** to pCheck. -*/ -static void checkPtrmap( - IntegrityCk *pCheck, /* Integrity check context */ - Pgno iChild, /* Child page number */ - u8 eType, /* Expected pointer map type */ - Pgno iParent, /* Expected pointer map parent page number */ - char *zContext /* Context description (used for error msg) */ -){ - int rc; - u8 ePtrmapType; - Pgno iPtrmapParent; - - rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1; - checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild); - return; - } - - if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ - checkAppendMsg(pCheck, zContext, - "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", - iChild, eType, iParent, ePtrmapType, iPtrmapParent); - } -} -#endif - -/* -** Check the integrity of the freelist or of an overflow page list. -** Verify that the number of pages on the list is N. -*/ -static void checkList( - IntegrityCk *pCheck, /* Integrity checking context */ - int isFreeList, /* True for a freelist. False for overflow page list */ - int iPage, /* Page number for first page in the list */ - int N, /* Expected number of pages in the list */ - char *zContext /* Context for error messages */ -){ - int i; - int expected = N; - int iFirst = iPage; - while( N-- > 0 && pCheck->mxErr ){ - DbPage *pOvflPage; - unsigned char *pOvflData; - if( iPage<1 ){ - checkAppendMsg(pCheck, zContext, - "%d of %d pages missing from overflow list starting at %d", - N+1, expected, iFirst); - break; - } - if( checkRef(pCheck, iPage, zContext) ) break; - if( sqlite4PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){ - checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); - break; - } - pOvflData = (unsigned char *)sqlite4PagerGetData(pOvflPage); - if( isFreeList ){ - int n = get4byte(&pOvflData[4]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pCheck->pBt->autoVacuum ){ - checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext); - } -#endif - if( n>(int)pCheck->pBt->usableSize/4-2 ){ - checkAppendMsg(pCheck, zContext, - "freelist leaf count too big on page %d", iPage); - N--; - }else{ - for(i=0; ipBt->autoVacuum ){ - checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext); - } -#endif - checkRef(pCheck, iFreePage, zContext); - } - N -= n; - } - } -#ifndef SQLITE_OMIT_AUTOVACUUM - else{ - /* If this database supports auto-vacuum and iPage is not the last - ** page in this overflow list, check that the pointer-map entry for - ** the following page matches iPage. - */ - if( pCheck->pBt->autoVacuum && N>0 ){ - i = get4byte(pOvflData); - checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext); - } - } -#endif - iPage = get4byte(pOvflData); - sqlite4PagerUnref(pOvflPage); - } -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Do various sanity checks on a single page of a tree. Return -** the tree depth. Root pages return 0. Parents of root pages -** return 1, and so forth. -** -** These checks are done: -** -** 1. Make sure that cells and freeblocks do not overlap -** but combine to completely cover the page. -** NO 2. Make sure cell keys are in order. -** NO 3. Make sure no key is less than or equal to zLowerBound. -** NO 4. Make sure no key is greater than or equal to zUpperBound. -** 5. Check the integrity of overflow pages. -** 6. Recursively call checkTreePage on all children. -** 7. Verify that the depth of all children is the same. -** 8. Make sure this page is at least 33% full or else it is -** the root of the tree. -*/ -static int checkTreePage( - IntegrityCk *pCheck, /* Context for the sanity check */ - int iPage, /* Page number of the page to check */ - char *zParentContext, /* Parent context */ - i64 *pnParentMinKey, - i64 *pnParentMaxKey -){ - MemPage *pPage; - int i, rc, depth, d2, pgno, cnt; - int hdr, cellStart; - int nCell; - u8 *data; - BtShared *pBt; - int usableSize; - char zContext[100]; - char *hit = 0; - i64 nMinKey = 0; - i64 nMaxKey = 0; - - sqlite4_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage); - - /* Check that the page exists - */ - pBt = pCheck->pBt; - usableSize = pBt->usableSize; - if( iPage==0 ) return 0; - if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ - checkAppendMsg(pCheck, zContext, - "unable to get the page. error code=%d", rc); - return 0; - } - - /* Clear MemPage.isInit to make sure the corruption detection code in - ** btreeInitPage() is executed. */ - pPage->isInit = 0; - if( (rc = btreeInitPage(pPage))!=0 ){ - assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ - checkAppendMsg(pCheck, zContext, - "btreeInitPage() returns error code %d", rc); - releasePage(pPage); - return 0; - } - - /* Check out all the cells. - */ - depth = 0; - for(i=0; inCell && pCheck->mxErr; i++){ - u8 *pCell; - u32 sz; - CellInfo info; - - /* Check payload overflow pages - */ - sqlite4_snprintf(sizeof(zContext), zContext, - "On tree page %d cell %d: ", iPage, i); - pCell = findCell(pPage,i); - btreeParseCellPtr(pPage, pCell, &info); - sz = info.nData; - if( !pPage->intKey ) sz += (int)info.nKey; - /* For intKey pages, check that the keys are in order. - */ - else if( i==0 ) nMinKey = nMaxKey = info.nKey; - else{ - if( info.nKey <= nMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey); - } - nMaxKey = info.nKey; - } - assert( sz==info.nPayload ); - if( (sz>info.nLocal) - && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) - ){ - int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); - Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext); - } -#endif - checkList(pCheck, 0, pgnoOvfl, nPage, zContext); - } - - /* Check sanity of left child page. - */ - if( !pPage->leaf ){ - pgno = get4byte(pCell); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); - } -#endif - d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey); - if( i>0 && d2!=depth ){ - checkAppendMsg(pCheck, zContext, "Child page depth differs"); - } - depth = d2; - } - } - - if( !pPage->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - sqlite4_snprintf(sizeof(zContext), zContext, - "On page %d at right child: ", iPage); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); - } -#endif - checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey); - } - - /* For intKey leaf pages, check that the min/max keys are in order - ** with any left/parent/right pages. - */ - if( pPage->leaf && pPage->intKey ){ - /* if we are a left child page */ - if( pnParentMinKey ){ - /* if we are the left most child page */ - if( !pnParentMaxKey ){ - if( nMaxKey > *pnParentMinKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (max larger than parent min of %lld)", - nMaxKey, *pnParentMinKey); - } - }else{ - if( nMinKey <= *pnParentMinKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (min less than parent min of %lld)", - nMinKey, *pnParentMinKey); - } - if( nMaxKey > *pnParentMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (max larger than parent max of %lld)", - nMaxKey, *pnParentMaxKey); - } - *pnParentMinKey = nMaxKey; - } - /* else if we're a right child page */ - } else if( pnParentMaxKey ){ - if( nMinKey <= *pnParentMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (min less than parent max of %lld)", - nMinKey, *pnParentMaxKey); - } - } - } - - /* Check for complete coverage of the page - */ - data = pPage->aData; - hdr = pPage->hdrOffset; - hit = sqlite4PageMalloc( pBt->pageSize ); - if( hit==0 ){ - pCheck->mallocFailed = 1; - }else{ - int contentOffset = get2byteNotZero(&data[hdr+5]); - assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - memset(hit+contentOffset, 0, usableSize-contentOffset); - memset(hit, 1, contentOffset); - nCell = get2byte(&data[hdr+3]); - cellStart = hdr + 12 - 4*pPage->leaf; - for(i=0; i=usableSize ){ - checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage); - }else{ - for(j=pc+size-1; j>=pc; j--) hit[j]++; - } - } - i = get2byte(&data[hdr+1]); - while( i>0 ){ - int size, j; - assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ - size = get2byte(&data[i+2]); - assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ - for(j=i+size-1; j>=i; j--) hit[j]++; - j = get2byte(&data[i]); - assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ - assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ - i = j; - } - for(i=cnt=0; i1 ){ - checkAppendMsg(pCheck, 0, - "Multiple uses for byte %d of page %d", i, iPage); - break; - } - } - if( cnt!=data[hdr+7] ){ - checkAppendMsg(pCheck, 0, - "Fragmentation of %d bytes reported as %d on page %d", - cnt, data[hdr+7], iPage); - } - } - sqlite4PageFree(hit); - releasePage(pPage); - return depth+1; -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** This routine does a complete check of the given BTree file. aRoot[] is -** an array of pages numbers were each page number is the root page of -** a table. nRoot is the number of entries in aRoot. -** -** A read-only or read-write transaction must be opened before calling -** this function. -** -** Write the number of error seen in *pnErr. Except for some memory -** allocation errors, an error message held in memory obtained from -** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is -** returned. If a memory allocation error occurs, NULL is returned. -*/ -char *sqlite4BtreeIntegrityCheck( - Btree *p, /* The btree to be checked */ - int *aRoot, /* An array of root pages numbers for individual trees */ - int nRoot, /* Number of entries in aRoot[] */ - int mxErr, /* Stop reporting errors after this many */ - int *pnErr /* Write number of errors seen to this variable */ -){ - Pgno i; - int nRef; - IntegrityCk sCheck; - BtShared *pBt = p->pBt; - char zErr[100]; - - sqlite4BtreeEnter(p); - assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); - nRef = sqlite4PagerRefcount(pBt->pPager); - sCheck.pBt = pBt; - sCheck.pPager = pBt->pPager; - sCheck.nPage = btreePagecount(sCheck.pBt); - sCheck.mxErr = mxErr; - sCheck.nErr = 0; - sCheck.mallocFailed = 0; - *pnErr = 0; - if( sCheck.nPage==0 ){ - sqlite4BtreeLeave(p); - return 0; - } - sCheck.anRef = sqlite4Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); - if( !sCheck.anRef ){ - *pnErr = 1; - sqlite4BtreeLeave(p); - return 0; - } - for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } - i = PENDING_BYTE_PAGE(pBt); - if( i<=sCheck.nPage ){ - sCheck.anRef[i] = 1; - } - sqlite4StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000); - sCheck.errMsg.useMalloc = 2; - - /* Check the integrity of the freelist - */ - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), - get4byte(&pBt->pPage1->aData[36]), "Main freelist: "); - - /* Check all the tables. - */ - for(i=0; (int)iautoVacuum && aRoot[i]>1 ){ - checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0); - } -#endif - checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL); - } - - /* Make sure every page in the file is referenced - */ - for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( sCheck.anRef[i]==0 ){ - checkAppendMsg(&sCheck, 0, "Page %d is never used", i); - } -#else - /* If the database supports auto-vacuum, make sure no tables contain - ** references to pointer-map pages. - */ - if( sCheck.anRef[i]==0 && - (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, 0, "Page %d is never used", i); - } - if( sCheck.anRef[i]!=0 && - (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i); - } -#endif - } - - /* Make sure this analysis did not leave any unref() pages. - ** This is an internal consistency check; an integrity check - ** of the integrity check. - */ - if( NEVER(nRef != sqlite4PagerRefcount(pBt->pPager)) ){ - checkAppendMsg(&sCheck, 0, - "Outstanding page count goes from %d to %d during this analysis", - nRef, sqlite4PagerRefcount(pBt->pPager) - ); - } - - /* Clean up and report errors. - */ - sqlite4BtreeLeave(p); - sqlite4_free(sCheck.anRef); - if( sCheck.mallocFailed ){ - sqlite4StrAccumReset(&sCheck.errMsg); - *pnErr = sCheck.nErr+1; - return 0; - } - *pnErr = sCheck.nErr; - if( sCheck.nErr==0 ) sqlite4StrAccumReset(&sCheck.errMsg); - return sqlite4StrAccumFinish(&sCheck.errMsg); -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -/* -** Return the full pathname of the underlying database file. -** -** The pager filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. -*/ -const char *sqlite4BtreeGetFilename(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlite4PagerFilename(p->pBt->pPager); -} - -/* -** Return the pathname of the journal file for this database. The return -** value of this routine is the same regardless of whether the journal file -** has been created or not. -** -** The pager journal filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. -*/ -const char *sqlite4BtreeGetJournalname(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlite4PagerJournalname(p->pBt->pPager); -} - -/* -** Return non-zero if a transaction is active. -*/ -int sqlite4BtreeIsInTrans(Btree *p){ - assert( p==0 || sqlite4_mutex_held(p->db->mutex) ); - return (p && (p->inTrans==TRANS_WRITE)); -} - -#ifndef SQLITE_OMIT_WAL -/* -** Run a checkpoint on the Btree passed as the first argument. -** -** Return SQLITE_LOCKED if this or any other connection has an open -** transaction on the shared-cache the argument Btree is connected to. -** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. -*/ -int sqlite4BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; - if( p ){ - BtShared *pBt = p->pBt; - sqlite4BtreeEnter(p); - if( pBt->inTransaction!=TRANS_NONE ){ - rc = SQLITE_LOCKED; - }else{ - rc = sqlite4PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); - } - sqlite4BtreeLeave(p); - } - return rc; -} -#endif - -/* -** Return non-zero if a read (or write) transaction is active. -*/ -int sqlite4BtreeIsInReadTrans(Btree *p){ - assert( p ); - assert( sqlite4_mutex_held(p->db->mutex) ); - return p->inTrans!=TRANS_NONE; -} - -int sqlite4BtreeIsInBackup(Btree *p){ - assert( p ); - assert( sqlite4_mutex_held(p->db->mutex) ); - return p->nBackup!=0; -} - -/* -** This function returns a pointer to a blob of memory associated with -** a single shared-btree. The memory is used by client code for its own -** purposes (for example, to store a high-level schema associated with -** the shared-btree). The btree layer manages reference counting issues. -** -** The first time this is called on a shared-btree, nBytes bytes of memory -** are allocated, zeroed, and returned to the caller. For each subsequent -** call the nBytes parameter is ignored and a pointer to the same blob -** of memory returned. -** -** If the nBytes parameter is 0 and the blob of memory has not yet been -** allocated, a null pointer is returned. If the blob has already been -** allocated, it is returned as normal. -** -** Just before the shared-btree is closed, the function passed as the -** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. The xFree function should not call sqlite4_free() -** on the memory, the btree layer does that. -*/ -void *sqlite4BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ - BtShared *pBt = p->pBt; - sqlite4BtreeEnter(p); - if( !pBt->pSchema && nBytes ){ - pBt->pSchema = sqlite4DbMallocZero(0, nBytes); - pBt->xFreeSchema = xFree; - } - sqlite4BtreeLeave(p); - return pBt->pSchema; -} - -/* -** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared -** btree as the argument handle holds an exclusive lock on the -** sqlite_master table. Otherwise SQLITE_OK. -*/ -int sqlite4BtreeSchemaLocked(Btree *p){ - int rc; - assert( sqlite4_mutex_held(p->db->mutex) ); - sqlite4BtreeEnter(p); - rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); - assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE ); - sqlite4BtreeLeave(p); - return rc; -} - - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Obtain a lock on the table whose root page is iTab. The -** lock is a write lock if isWritelock is true or a read lock -** if it is false. -*/ -int sqlite4BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ - int rc = SQLITE_OK; - assert( p->inTrans!=TRANS_NONE ); - if( p->sharable ){ - u8 lockType = READ_LOCK + isWriteLock; - assert( READ_LOCK+1==WRITE_LOCK ); - assert( isWriteLock==0 || isWriteLock==1 ); - - sqlite4BtreeEnter(p); - rc = querySharedCacheTableLock(p, iTab, lockType); - if( rc==SQLITE_OK ){ - rc = setSharedCacheTableLock(p, iTab, lockType); - } - sqlite4BtreeLeave(p); - } - return rc; -} -#endif - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Argument pCsr must be a cursor opened for writing on an -** INTKEY table currently pointing at a valid table entry. -** This function modifies the data stored as part of that entry. -** -** Only the data content may only be modified, it is not possible to -** change the length of the data stored. If this function is called with -** parameters that attempt to write past the end of the existing data, -** no modifications are made and SQLITE_CORRUPT is returned. -*/ -int sqlite4BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ - int rc; - assert( cursorHoldsMutex(pCsr) ); - assert( sqlite4_mutex_held(pCsr->pBtree->db->mutex) ); - assert( pCsr->isIncrblobHandle ); - - rc = restoreCursorPosition(pCsr); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pCsr->eState!=CURSOR_REQUIRESEEK ); - if( pCsr->eState!=CURSOR_VALID ){ - return SQLITE_ABORT; - } - - /* Check some assumptions: - ** (a) the cursor is open for writing, - ** (b) there is a read/write transaction open, - ** (c) the connection holds a write-lock on the table (if required), - ** (d) there are no conflicting read-locks, and - ** (e) the cursor points at a valid row of an intKey table. - */ - if( !pCsr->wrFlag ){ - return SQLITE_READONLY; - } - assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 - && pCsr->pBt->inTransaction==TRANS_WRITE ); - assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); - assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); - assert( pCsr->apPage[pCsr->iPage]->intKey ); - - return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); -} - -/* -** Set a flag on this cursor to cache the locations of pages from the -** overflow list for the current row. This is used by cursors opened -** for incremental blob IO only. -** -** This function sets a flag only. The actual page location cache -** (stored in BtCursor.aOverflow[]) is allocated and used by function -** accessPayload() (the worker function for sqlite4BtreeData() and -** sqlite4BtreePutData()). -*/ -void sqlite4BtreeCacheOverflow(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( sqlite4_mutex_held(pCur->pBtree->db->mutex) ); - invalidateOverflowCache(pCur); - pCur->isIncrblobHandle = 1; -} -#endif - -/* -** Set both the "read version" (single byte at byte offset 18) and -** "write version" (single byte at byte offset 19) fields in the database -** header to iVersion. -*/ -int sqlite4BtreeSetVersion(Btree *pBtree, int iVersion){ - BtShared *pBt = pBtree->pBt; - int rc; /* Return code */ - - assert( iVersion==1 || iVersion==2 ); - - /* If setting the version fields to 1, do not automatically open the - ** WAL connection, even if the version fields are currently set to 2. - */ - pBt->btsFlags &= ~BTS_NO_WAL; - if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL; - - rc = sqlite4BtreeBeginTrans(pBtree, 0); - if( rc==SQLITE_OK ){ - u8 *aData = pBt->pPage1->aData; - if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ - rc = sqlite4BtreeBeginTrans(pBtree, 2); - if( rc==SQLITE_OK ){ - rc = sqlite4PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - aData[18] = (u8)iVersion; - aData[19] = (u8)iVersion; - } - } - } - } - - pBt->btsFlags &= ~BTS_NO_WAL; - return rc; -} DELETED src/btree.h Index: src/btree.h ================================================================== --- src/btree.h +++ /dev/null @@ -1,240 +0,0 @@ -/* -** 2001 September 15 -** -** 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 header file defines the interface that the sqlite B-Tree file -** subsystem. See comments in the source code for a detailed description -** of what each interface routine does. -*/ -#ifndef _BTREE_H_ -#define _BTREE_H_ - -/* TODO: This definition is just included so other modules compile. It -** needs to be revisited. -*/ -#define SQLITE_N_BTREE_META 10 - -/* -** If defined as non-zero, auto-vacuum is enabled by default. Otherwise -** it must be turned on for each database using "PRAGMA auto_vacuum = 1". -*/ -#ifndef SQLITE_DEFAULT_AUTOVACUUM - #define SQLITE_DEFAULT_AUTOVACUUM 0 -#endif - -#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ -#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ -#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ - -/* -** Forward declarations of structure -*/ -typedef struct Btree Btree; -typedef struct BtCursor BtCursor; -typedef struct BtShared BtShared; - - -int sqlite4BtreeOpen( - sqlite4_vfs *pVfs, /* VFS to use with this b-tree */ - const char *zFilename, /* Name of database file to open */ - sqlite4 *db, /* Associated database connection */ - Btree **ppBtree, /* Return open Btree* here */ - int flags, /* Flags */ - int vfsFlags /* Flags passed through to VFS open */ -); - -/* The flags parameter to sqlite4BtreeOpen can be the bitwise or of the -** following values. -** -** NOTE: These values must match the corresponding PAGER_ values in -** pager.h. -*/ -#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ -#define BTREE_MEMORY 2 /* This is an in-memory DB */ -#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */ -#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */ - -int sqlite4BtreeClose(Btree*); -int sqlite4BtreeSetCacheSize(Btree*,int); -int sqlite4BtreeSetSafetyLevel(Btree*,int,int,int); -int sqlite4BtreeSyncDisabled(Btree*); -int sqlite4BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); -int sqlite4BtreeGetPageSize(Btree*); -int sqlite4BtreeMaxPageCount(Btree*,int); -u32 sqlite4BtreeLastPage(Btree*); -int sqlite4BtreeSecureDelete(Btree*,int); -int sqlite4BtreeGetReserve(Btree*); -int sqlite4BtreeSetAutoVacuum(Btree *, int); -int sqlite4BtreeGetAutoVacuum(Btree *); -int sqlite4BtreeBeginTrans(Btree*,int); -int sqlite4BtreeCommitPhaseOne(Btree*, const char *zMaster); -int sqlite4BtreeCommitPhaseTwo(Btree*, int); -int sqlite4BtreeCommit(Btree*); -int sqlite4BtreeRollback(Btree*); -int sqlite4BtreeBeginStmt(Btree*,int); -int sqlite4BtreeCreateTable(Btree*, int*, int flags); -int sqlite4BtreeIsInTrans(Btree*); -int sqlite4BtreeIsInReadTrans(Btree*); -int sqlite4BtreeIsInBackup(Btree*); -void *sqlite4BtreeSchema(Btree *, int, void(*)(void *)); -int sqlite4BtreeSchemaLocked(Btree *pBtree); -int sqlite4BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); -int sqlite4BtreeSavepoint(Btree *, int, int); - -const char *sqlite4BtreeGetFilename(Btree *); -const char *sqlite4BtreeGetJournalname(Btree *); -int sqlite4BtreeCopyFile(Btree *, Btree *); - -int sqlite4BtreeIncrVacuum(Btree *); - -/* The flags parameter to sqlite4BtreeCreateTable can be the bitwise OR -** 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_BLOBKEY 2 /* Table has keys only - no data */ - -int sqlite4BtreeDropTable(Btree*, int, int*); -int sqlite4BtreeClearTable(Btree*, int, int*); -void sqlite4BtreeTripAllCursors(Btree*, int); - -void sqlite4BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); -int sqlite4BtreeUpdateMeta(Btree*, int idx, u32 value); - -/* -** The second parameter to sqlite4BtreeGetMeta or sqlite4BtreeUpdateMeta -** should be one of the following values. The integer values are assigned -** to constants so that the offset of the corresponding field in an -** SQLite database header may be found using the following formula: -** -** offset = 36 + (idx * 4) -** -** For example, the free-page-count field is located at byte offset 36 of -** the database file header. The incr-vacuum-flag field is located at -** byte offset 64 (== 36+4*7). -*/ -#define BTREE_FREE_PAGE_COUNT 0 -#define BTREE_SCHEMA_VERSION 1 -#define BTREE_FILE_FORMAT 2 -#define BTREE_DEFAULT_CACHE_SIZE 3 -#define BTREE_LARGEST_ROOT_PAGE 4 -#define BTREE_TEXT_ENCODING 5 -#define BTREE_USER_VERSION 6 -#define BTREE_INCR_VACUUM 7 - -int sqlite4BtreeCursor( - Btree*, /* BTree containing table to open */ - int iTable, /* Index of root page */ - int wrFlag, /* 1 for writing. 0 for read-only */ - struct KeyInfo*, /* First argument to compare function */ - BtCursor *pCursor /* Space to write cursor structure */ -); -int sqlite4BtreeCursorSize(void); -void sqlite4BtreeCursorZero(BtCursor*); - -int sqlite4BtreeCloseCursor(BtCursor*); -int sqlite4BtreeMovetoUnpacked( - BtCursor*, - UnpackedRecord *pUnKey, - i64 intKey, - int bias, - int *pRes -); -int sqlite4BtreeCursorHasMoved(BtCursor*, int*); -int sqlite4BtreeDelete(BtCursor*); -int sqlite4BtreeInsert(BtCursor*, const void *pKey, i64 nKey, - const void *pData, int nData, - int nZero, int bias, int seekResult); -int sqlite4BtreeFirst(BtCursor*, int *pRes); -int sqlite4BtreeLast(BtCursor*, int *pRes); -int sqlite4BtreeNext(BtCursor*, int *pRes); -int sqlite4BtreeEof(BtCursor*); -int sqlite4BtreePrevious(BtCursor*, int *pRes); -int sqlite4BtreeKeySize(BtCursor*, i64 *pSize); -int sqlite4BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -const void *sqlite4BtreeKeyFetch(BtCursor*, int *pAmt); -const void *sqlite4BtreeDataFetch(BtCursor*, int *pAmt); -int sqlite4BtreeDataSize(BtCursor*, u32 *pSize); -int sqlite4BtreeData(BtCursor*, u32 offset, u32 amt, void*); -void sqlite4BtreeSetCachedRowid(BtCursor*, sqlite4_int64); -sqlite4_int64 sqlite4BtreeGetCachedRowid(BtCursor*); - -char *sqlite4BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); -struct Pager *sqlite4BtreePager(Btree*); - -int sqlite4BtreePutData(BtCursor*, u32 offset, u32 amt, void*); -void sqlite4BtreeCacheOverflow(BtCursor *); -void sqlite4BtreeClearCursor(BtCursor *); - -int sqlite4BtreeSetVersion(Btree *pBt, int iVersion); - -#ifndef NDEBUG -int sqlite4BtreeCursorIsValid(BtCursor*); -#endif - -#ifndef SQLITE_OMIT_BTREECOUNT -int sqlite4BtreeCount(BtCursor *, i64 *); -#endif - -#ifdef SQLITE_TEST -int sqlite4BtreeCursorInfo(BtCursor*, int*, int); -void sqlite4BtreeCursorList(Btree*); -#endif - -#ifndef SQLITE_OMIT_WAL - int sqlite4BtreeCheckpoint(Btree*, int, int *, int *); -#endif - -/* -** If we are not using shared cache, then there is no need to -** use mutexes to access the BtShared structures. So make the -** Enter and Leave procedures no-ops. -*/ -#ifndef SQLITE_OMIT_SHARED_CACHE - void sqlite4BtreeEnter(Btree*); - void sqlite4BtreeEnterAll(sqlite4*); -#else -# define sqlite4BtreeEnter(X) -# define sqlite4BtreeEnterAll(X) -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE - int sqlite4BtreeSharable(Btree*); - void sqlite4BtreeLeave(Btree*); - void sqlite4BtreeEnterCursor(BtCursor*); - void sqlite4BtreeLeaveCursor(BtCursor*); - void sqlite4BtreeLeaveAll(sqlite4*); -#ifndef NDEBUG - /* These routines are used inside assert() statements only. */ - int sqlite4BtreeHoldsMutex(Btree*); - int sqlite4BtreeHoldsAllMutexes(sqlite4*); - int sqlite4SchemaMutexHeld(sqlite4*,int,Schema*); -#endif -#else - -# define sqlite4BtreeSharable(X) 0 -# define sqlite4BtreeLeave(X) -# define sqlite4BtreeEnterCursor(X) -# define sqlite4BtreeLeaveCursor(X) -# define sqlite4BtreeLeaveAll(X) - -# define sqlite4BtreeHoldsMutex(X) 1 -# define sqlite4BtreeHoldsAllMutexes(X) 1 -# define sqlite4SchemaMutexHeld(X,Y,Z) 1 -#endif - - -#endif /* _BTREE_H_ */ DELETED src/btreeInt.h Index: src/btreeInt.h ================================================================== --- src/btreeInt.h +++ /dev/null @@ -1,652 +0,0 @@ -/* -** 2004 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements a external (disk-based) database using BTrees. -** For a detailed discussion of BTrees, refer to -** -** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: -** "Sorting And Searching", pages 473-480. Addison-Wesley -** Publishing Company, Reading, Massachusetts. -** -** The basic idea is that each page of the file contains N database -** entries and N+1 pointers to subpages. -** -** ---------------------------------------------------------------- -** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) | -** ---------------------------------------------------------------- -** -** All of the keys on the page that Ptr(0) points to have values less -** than Key(0). All of the keys on page Ptr(1) and its subpages have -** values greater than Key(0) and less than Key(1). All of the keys -** on Ptr(N) and its subpages have values greater than Key(N-1). And -** so forth. -** -** Finding a particular key requires reading O(log(M)) pages from the -** disk where M is the number of entries in the tree. -** -** In this implementation, a single file can hold one or more separate -** BTrees. Each BTree is identified by the index of its root page. The -** key and data for any entry are combined to form the "payload". A -** fixed amount of payload can be carried directly on the database -** page. If the payload is larger than the preset amount then surplus -** bytes are stored on overflow pages. The payload for an entry -** and the preceding pointer are combined to form a "Cell". Each -** page has a small header which contains the Ptr(N) pointer and other -** information such as the size of key and data. -** -** FORMAT DETAILS -** -** The file is divided into pages. The first page is called page 1, -** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be any power of 2 between 512 and 65536. -** Each page can be either a btree page, a freelist page, an overflow -** page, or a pointer-map page. -** -** The first page is always a btree page. The first 100 bytes of the first -** page contain a special header (the "file header") that describes the file. -** The format of the file header is as follows: -** -** OFFSET SIZE DESCRIPTION -** 0 16 Header string: "SQLite format 4\000" -** 16 2 Page size in bytes. -** 18 1 File format write version -** 19 1 File format read version -** 20 1 Bytes of unused space at the end of each page -** 21 1 Max embedded payload fraction -** 22 1 Min embedded payload fraction -** 23 1 Min leaf payload fraction -** 24 4 File change counter -** 28 4 Reserved for future use -** 32 4 First freelist page -** 36 4 Number of freelist pages in the file -** 40 60 15 4-byte meta values passed to higher layers -** -** 40 4 Schema cookie -** 44 4 File format of schema layer -** 48 4 Size of page cache -** 52 4 Largest root-page (auto/incr_vacuum) -** 56 4 1=UTF-8 2=UTF16le 3=UTF16be -** 60 4 User version -** 64 4 Incremental vacuum mode -** 68 4 unused -** 72 4 unused -** 76 4 unused -** -** All of the integer values are big-endian (most significant byte first). -** -** The file change counter is incremented when the database is changed -** This counter allows other processes to know when the file has changed -** and thus when they need to flush their cache. -** -** The max embedded payload fraction is the amount of the total usable -** space in a page that can be consumed by a single cell for standard -** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default -** is to limit the maximum cell size so that at least 4 cells will fit -** on one page. Thus the default max embedded payload fraction is 64. -** -** If the payload for a cell is larger than the max payload, then extra -** payload is spilled to overflow pages. Once an overflow page is allocated, -** as many bytes as possible are moved into the overflow pages without letting -** the cell size drop below the min embedded payload fraction. -** -** The min leaf payload fraction is like the min embedded payload fraction -** except that it applies to leaf nodes in a LEAFDATA tree. The maximum -** payload fraction for a LEAFDATA tree is always 100% (or 255) and it -** not specified in the header. -** -** Each btree pages is divided into three sections: The header, the -** cell pointer array, and the cell content area. Page 1 also has a 100-byte -** file header that occurs before the page header. -** -** |----------------| -** | file header | 100 bytes. Page 1 only. -** |----------------| -** | page header | 8 bytes for leaves. 12 bytes for interior nodes -** |----------------| -** | cell pointer | | 2 bytes per cell. Sorted order. -** | array | | Grows downward -** | | v -** |----------------| -** | unallocated | -** | space | -** |----------------| ^ Grows upwards -** | cell content | | Arbitrary order interspersed with freeblocks. -** | area | | and free space fragments. -** |----------------| -** -** The page headers looks like this: -** -** OFFSET SIZE DESCRIPTION -** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf -** 1 2 byte offset to the first freeblock -** 3 2 number of cells on this page -** 5 2 first byte of the cell content area -** 7 1 number of fragmented free bytes -** 8 4 Right child (the Ptr(N) value). Omitted on leaves. -** -** The flags define the format of this btree page. The leaf flag means that -** this page has no children. The zerodata flag means that this page carries -** only keys and no data. The intkey flag means that the key is a integer -** which is stored in the key size entry of the cell header rather than in -** the payload area. -** -** The cell pointer array begins on the first byte after the page header. -** The cell pointer array contains zero or more 2-byte numbers which are -** offsets from the beginning of the page to the cell content in the cell -** content area. The cell pointers occur in sorted order. The system strives -** to keep free space after the last cell pointer so that new cells can -** be easily added without having to defragment the page. -** -** Cell content is stored at the very end of the page and grows toward the -** beginning of the page. -** -** Unused space within the cell content area is collected into a linked list of -** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset -** to the first freeblock is given in the header. Freeblocks occur in -** increasing order. Because a freeblock must be at least 4 bytes in size, -** any group of 3 or fewer unused bytes in the cell content area cannot -** exist on the freeblock chain. A group of 3 or fewer free bytes is called -** a fragment. The total number of bytes in all fragments is recorded. -** in the page header at offset 7. -** -** SIZE DESCRIPTION -** 2 Byte offset of the next freeblock -** 2 Bytes in this freeblock -** -** Cells are of variable length. Cells are stored in the cell content area at -** the end of the page. Pointers to the cells are in the cell pointer array -** that immediately follows the page header. Cells is not necessarily -** contiguous or in order, but cell pointers are contiguous and in order. -** -** Cell content makes use of variable length integers. A variable -** length integer is 1 to 9 bytes where the lower 7 bits of each -** byte are used. The integer consists of all bytes that have bit 8 set and -** the first byte with bit 8 clear. The most significant byte of the integer -** appears first. A variable-length integer may not be more than 9 bytes long. -** As a special case, all 8 bytes of the 9th byte are used as data. This -** allows a 64-bit integer to be encoded in 9 bytes. -** -** 0x00 becomes 0x00000000 -** 0x7f becomes 0x0000007f -** 0x81 0x00 becomes 0x00000080 -** 0x82 0x00 becomes 0x00000100 -** 0x80 0x7f becomes 0x0000007f -** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 -** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 -** -** Variable length integers are used for rowids and to hold the number of -** bytes of key and data in a btree cell. -** -** The content of a cell looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of the left child. Omitted if leaf flag is set. -** var Number of bytes of data. Omitted if the zerodata flag is set. -** var Number of bytes of key. Or the key itself if intkey flag is set. -** * Payload -** 4 First page of the overflow chain. Omitted if no overflow -** -** Overflow pages form a linked list. Each page except the last is completely -** filled with data (pagesize - 4 bytes). The last page can have as little -** as 1 byte of data. -** -** SIZE DESCRIPTION -** 4 Page number of next overflow page -** * Data -** -** Freelist pages come in two subtypes: trunk pages and leaf pages. The -** file header points to the first in a linked list of trunk page. Each trunk -** page points to multiple leaf pages. The content of a leaf page is -** unspecified. A trunk page looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of next trunk page -** 4 Number of leaf pointers on this page -** * zero or more pages numbers of leaves -*/ -#include "sqliteInt.h" - - -/* The following value is the maximum cell size assuming a maximum page -** size give above. -*/ -#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) - -/* The maximum number of cells on a single page of the database. This -** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself -** plus 2 bytes for the index to the cell in the page header). Such -** small cells will be rare, but they are possible. -*/ -#define MX_CELL(pBt) ((pBt->pageSize-8)/6) - -/* Forward declarations */ -typedef struct MemPage MemPage; -typedef struct BtLock BtLock; - -/* -** This is a magic string that appears at the beginning of every -** SQLite database in order to identify the file as a real database. -** -** You can change this value at compile-time by specifying a -** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The -** header must be exactly 16 bytes including the zero-terminator so -** the string itself should be 15 characters long. If you change -** the header, then your custom library will not be able to read -** databases generated by the standard tools and the standard tools -** will not be able to read databases created by your custom library. -*/ -#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */ -# define SQLITE_FILE_HEADER "SQLite format 4" -#endif - -/* -** Page type flags. An ORed combination of these flags appear as the -** first byte of on-disk image of every BTree page. -*/ -#define PTF_INTKEY 0x01 -#define PTF_ZERODATA 0x02 -#define PTF_LEAFDATA 0x04 -#define PTF_LEAF 0x08 - -/* -** As each page of the file is loaded into memory, an instance of the following -** structure is appended and initialized to zero. This structure stores -** information about the page that is decoded from the raw file page. -** -** The pParent field points back to the parent page. This allows us to -** walk up the BTree from any leaf to the root. Care must be taken to -** unref() the parent page pointer when this page is no longer referenced. -** The pageDestructor() routine handles that chore. -** -** Access to all fields of this structure is controlled by the mutex -** stored in MemPage.pBt->mutex. -*/ -struct MemPage { - u8 isInit; /* True if previously initialized. MUST BE FIRST! */ - u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ - u8 intKey; /* True if intkey flag is set */ - u8 leaf; /* True if leaf flag is set */ - u8 hasData; /* True if this page stores data */ - u8 hdrOffset; /* 100 for page 1. 0 otherwise */ - u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ - u8 max1bytePayload; /* min(maxLocal,127) */ - u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ - u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ - u16 cellOffset; /* Index in aData of first cell pointer */ - u16 nFree; /* Number of free bytes on the page */ - u16 nCell; /* Number of cells on this page, local and ovfl */ - u16 maskPage; /* Mask for page offset */ - struct _OvflCell { /* Cells that will not fit on aData[] */ - u8 *pCell; /* Pointers to the body of the overflow cell */ - u16 idx; /* Insert this cell before idx-th non-overflow cell */ - } aOvfl[5]; - BtShared *pBt; /* Pointer to BtShared that this page is part of */ - u8 *aData; /* Pointer to disk image of the page data */ - u8 *aDataEnd; /* One byte past the end of usable data */ - u8 *aCellIdx; /* The cell index area */ - DbPage *pDbPage; /* Pager page handle */ - Pgno pgno; /* Page number for this page */ -}; - -/* -** The in-memory image of a disk page has the auxiliary information appended -** to the end. EXTRA_SIZE is the number of bytes of space needed to hold -** that extra information. -*/ -#define EXTRA_SIZE sizeof(MemPage) - -/* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; - -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 - -/* A Btree handle -** -** A database connection contains a pointer to an instance of -** this object for every database file that it has open. This structure -** is opaque to the database connection. The database connection cannot -** see the internals of this structure and only deals with pointers to -** this structure. -** -** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each connection -** has it own instance of this object. But each instance of this object -** points to the same BtShared object. The database cache and the -** schema associated with the database file are all contained within -** the BtShared object. -** -** All fields in this structure are accessed under sqlite4.mutex. -** The pBt pointer itself may not be changed while there exists cursors -** in the referenced BtShared that point back to this Btree since those -** cursors have to go through this Btree to find their BtShared and -** they often do so without holding sqlite4.mutex. -*/ -struct Btree { - sqlite4 *db; /* The database connection holding this btree */ - BtShared *pBt; /* Sharable content of this btree */ - u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ - u8 sharable; /* True if we can share pBt with another db */ - u8 locked; /* True if db currently has pBt locked */ - int wantToLock; /* Number of nested calls to sqlite4BtreeEnter() */ - int nBackup; /* Number of backup operations reading this btree */ - Btree *pNext; /* List of other sharable Btrees from the same db */ - Btree *pPrev; /* Back pointer of the same list */ -#ifndef SQLITE_OMIT_SHARED_CACHE - BtLock lock; /* Object used to lock page 1 */ -#endif -}; - -/* -** Btree.inTrans may take one of the following values. -** -** If the shared-data extension is enabled, there may be multiple users -** of the Btree structure. At most one of these may open a write transaction, -** but any number may have active read transactions. -*/ -#define TRANS_NONE 0 -#define TRANS_READ 1 -#define TRANS_WRITE 2 - -/* -** An instance of this object represents a single database file. -** -** A single database file can be in use at the same time by two -** or more database connections. When two or more connections are -** sharing the same database file, each connection has it own -** private Btree object for the file and each of those Btrees points -** to this one BtShared object. BtShared.nRef is the number of -** connections currently sharing this database file. -** -** Fields in this structure are accessed under the BtShared.mutex -** mutex, except for nRef and pNext which are accessed under the -** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field -** may not be modified once it is initially set as long as nRef>0. -** The pSchema field may be set once under BtShared.mutex and -** thereafter is unchanged as long as nRef>0. -** -** isPending: -** -** If a BtShared client fails to obtain a write-lock on a database -** table (because there exists one or more read-locks on the table), -** the shared-cache enters 'pending-lock' state and isPending is -** set to true. -** -** The shared-cache leaves the 'pending lock' state when either of -** the following occur: -** -** 1) The current writer (BtShared.pWriter) concludes its transaction, OR -** 2) The number of locks held by other connections drops to zero. -** -** while in the 'pending-lock' state, no connection may start a new -** transaction. -** -** This feature is included to help prevent writer-starvation. -*/ -struct BtShared { - Pager *pPager; /* The page cache */ - sqlite4 *db; /* Database connection currently using this Btree */ - BtCursor *pCursor; /* A list of all open cursors */ - MemPage *pPage1; /* First page of the database */ - u8 openFlags; /* Flags to sqlite4BtreeOpen() */ -#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 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ - u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ - 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 */ - 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) */ - u32 nPage; /* Number of pages in the database */ - void *pSchema; /* Pointer to space allocated by sqlite4BtreeSchema() */ - void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlite4_mutex *mutex; /* Non-recursive mutex required to access this object */ - Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ -#ifndef SQLITE_OMIT_SHARED_CACHE - int nRef; /* Number of references to this structure */ - BtShared *pNext; /* Next on a list of sharable BtShared structs */ - BtLock *pLock; /* List of locks held on this shared-btree struct */ - Btree *pWriter; /* Btree with currently open write transaction */ -#endif - u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ -}; - -/* -** Allowed values for BtShared.btsFlags -*/ -#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ -#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ -#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ -#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */ -#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */ -#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */ -#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */ - -/* -** An instance of the following structure is used to hold information -** about a cell. The parseCellPtr() function fills in this structure -** based on information extract from the raw disk page. -*/ -typedef struct CellInfo CellInfo; -struct CellInfo { - 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 */ - u16 nLocal; /* Amount of payload held locally */ - u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */ - u16 nSize; /* Size of the cell content on the main b-tree page */ -}; - -/* -** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than -** this will be declared corrupt. This value is calculated based on a -** maximum database size of 2^31 pages a minimum fanout of 2 for a -** root-node and 3 for all other internal nodes. -** -** If a tree that appears to be taller than this is encountered, it is -** assumed that the database is corrupt. -*/ -#define BTCURSOR_MAX_DEPTH 20 - -/* -** A cursor is a pointer to a particular entry within a particular -** b-tree within a database file. -** -** The entry is identified by its MemPage and the index in -** MemPage.aCell[] of the entry. -** -** A single database file can be shared by two more database connections, -** but cursors cannot be shared. Each cursor is associated with a -** particular database connection identified BtCursor.pBtree.db. -** -** Fields in this structure are accessed under the BtShared.mutex -** found at self->pBt->mutex. -*/ -struct BtCursor { - Btree *pBtree; /* The Btree to which this cursor belongs */ - BtShared *pBt; /* The BtShared this cursor points to */ - BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ - struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ - Pgno pgnoRoot; /* The root page of this tree */ - sqlite4_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) */ -#ifndef SQLITE_OMIT_INCRBLOB - 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 */ - u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ - MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ -}; - -/* -** Potential values for BtCursor.eState. -** -** CURSOR_VALID: -** Cursor points to a valid entry. getPayload() etc. may be called. -** -** CURSOR_INVALID: -** Cursor does not point to a valid entry. This can happen (for example) -** because the table is empty or because BtreeCursorFirst() has not been -** called. -** -** CURSOR_REQUIRESEEK: -** The table that this cursor was opened on still exists, but has been -** modified since the cursor was last used. The cursor position is saved -** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in -** this state, restoreCursorPosition() can be called to attempt to -** seek the cursor to the saved position. -** -** CURSOR_FAULT: -** A unrecoverable error (an I/O error or a malloc failure) has occurred -** on a different connection that shares the BtShared cache with this -** cursor. The error has left the cache in an inconsistent state. -** Do nothing else with this cursor. Any attempt to use the cursor -** should return the error code stored in BtCursor.skip -*/ -#define CURSOR_INVALID 0 -#define CURSOR_VALID 1 -#define CURSOR_REQUIRESEEK 2 -#define CURSOR_FAULT 3 - -/* -** The database page the PENDING_BYTE occupies. This page is never used. -*/ -# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) - -/* -** These macros define the location of the pointer-map entry for a -** database page. The first argument to each is the number of usable -** bytes on each page of the database (often 1024). The second is the -** page number to look up in the pointer map. -** -** PTRMAP_PAGENO returns the database page number of the pointer-map -** page that stores the required pointer. PTRMAP_PTROFFSET returns -** the offset of the requested map entry. -** -** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page, -** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be -** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements -** this test. -*/ -#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) -#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1)) -#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) - -/* -** The pointer map is a lookup table that identifies the parent page for -** each child page in the database file. The parent page is the page that -** contains a pointer to the child. Every page in the database contains -** 0 or 1 parent pages. (In this context 'database page' refers -** to any page that is not part of the pointer map itself.) Each pointer map -** entry consists of a single byte 'type' and a 4 byte parent page number. -** The PTRMAP_XXX identifiers below are the valid types. -** -** The purpose of the pointer map is to facility moving pages from one -** position in the file to another as part of autovacuum. When a page -** is moved, the pointer in its parent must be updated to point to the -** new location. The pointer map is used to locate the parent page quickly. -** -** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not -** used in this case. -** -** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number -** is not used in this case. -** -** PTRMAP_OVERFLOW1: The database page is the first page in a list of -** overflow pages. The page number identifies the page that -** contains the cell with a pointer to this overflow page. -** -** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of -** overflow pages. The page-number identifies the previous -** page in the overflow page list. -** -** PTRMAP_BTREE: The database page is a non-root btree page. The page number -** identifies the parent page in the btree. -*/ -#define PTRMAP_ROOTPAGE 1 -#define PTRMAP_FREEPAGE 2 -#define PTRMAP_OVERFLOW1 3 -#define PTRMAP_OVERFLOW2 4 -#define PTRMAP_BTREE 5 - -/* A bunch of assert() statements to check the transaction state variables -** of handle p (type Btree*) are internally consistent. -*/ -#define btreeIntegrity(p) \ - assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ - assert( p->pBt->inTransaction>=p->inTrans ); - - -/* -** The ISAUTOVACUUM macro is used within balance_nonroot() to determine -** if the database supports auto-vacuum or not. Because it is used -** within an expression that is an argument to another macro -** (sqliteMallocRaw), it is not possible to use conditional compilation. -** So, this macro is defined instead. -*/ -#ifndef SQLITE_OMIT_AUTOVACUUM -#define ISAUTOVACUUM (pBt->autoVacuum) -#else -#define ISAUTOVACUUM 0 -#endif - - -/* -** This structure is passed around through all the sanity checking routines -** in order to keep track of some global state information. -*/ -typedef struct IntegrityCk IntegrityCk; -struct IntegrityCk { - BtShared *pBt; /* The tree being checked out */ - Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ - Pgno nPage; /* Number of pages in the database */ - int *anRef; /* Number of times each page is referenced */ - int mxErr; /* Stop accumulating errors when this reaches zero */ - int nErr; /* Number of messages written to zErrMsg so far */ - int mallocFailed; /* A memory allocation error has occurred */ - StrAccum errMsg; /* Accumulate the error message text here */ -}; - -/* -** Routines to read or write a two- and four-byte big-endian integer values. -*/ -#define get2byte(x) ((x)[0]<<8 | (x)[1]) -#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) -#define get4byte sqlite4Get4byte -#define put4byte sqlite4Put4byte Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -151,14 +151,12 @@ yDbMask mask; int iDb; sqlite4VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; - sqlite4VdbeUsesBtree(v, iDb); sqlite4VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); if( db->init.busy==0 ){ - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); sqlite4VdbeAddOp3(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb], db->aDb[iDb].pSchema->iGeneration); } } @@ -269,15 +267,13 @@ int i; int nName; assert( zName!=0 ); nName = sqlite4Strlen30(zName); /* All mutexes are required for schema access. Make sure we hold them. */ - assert( zDatabase!=0 || sqlite4BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqlite4StrICmp(zDatabase, db->aDb[j].zName) ) continue; - assert( sqlite4SchemaMutexHeld(db, j, 0) ); p = sqlite4HashFind(&db->aDb[j].pSchema->tblHash, zName, nName); if( p ) break; } return p; } @@ -334,17 +330,15 @@ Index *sqlite4FindIndex(sqlite4 *db, const char *zName, const char *zDb){ Index *p = 0; int i; int nName = sqlite4Strlen30(zName); /* All mutexes are required for schema access. Make sure we hold them. */ - assert( zDb!=0 || sqlite4BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ Schema *pSchema = db->aDb[j].pSchema; assert( pSchema ); if( zDb && sqlite4StrICmp(zDb, db->aDb[j].zName) ) continue; - assert( sqlite4SchemaMutexHeld(db, j, 0) ); p = sqlite4HashFind(&pSchema->idxHash, zName, nName); if( p ) break; } return p; } @@ -369,11 +363,10 @@ void sqlite4UnlinkAndDeleteIndex(sqlite4 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; Hash *pHash; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite4Strlen30(zIdxName); pIndex = sqlite4HashInsert(pHash, zIdxName, len, 0); if( ALWAYS(pIndex) ){ if( pIndex->pTable->pIndex==pIndex ){ @@ -409,11 +402,10 @@ assert( iDbnDb ); if( iDb>=0 ){ /* Case 1: Reset the single schema identified by iDb */ Db *pDb = &db->aDb[iDb]; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); assert( pDb->pSchema!=0 ); sqlite4SchemaClear(pDb->pSchema); /* If any database other than TEMP is reset, then also reset TEMP ** since TEMP might be holding triggers that reference tables in the @@ -427,30 +419,28 @@ return; } /* Case 2 (from here to the end): Reset all schemas for all attached ** databases. */ assert( iDb<0 ); - sqlite4BtreeEnterAll(db); for(i=0; inDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ sqlite4SchemaClear(pDb->pSchema); } } db->flags &= ~SQLITE_InternChanges; sqlite4VtabUnlockList(db); - sqlite4BtreeLeaveAll(db); /* If one or more of the auxiliary database files has been closed, ** then remove them from the auxiliary database list. We take the ** opportunity to do this here since we have just deleted all of the ** schema hash tables and therefore do not have to make any changes ** to any of those tables. */ for(i=j=2; inDb; i++){ struct Db *pDb = &db->aDb[i]; - if( pDb->pBt==0 ){ + if( pDb->pKV==0 ){ sqlite4DbFree(db, pDb->zName); pDb->zName = 0; continue; } if( jpnBytesFreed==0 ){ char *zName = pIndex->zName; TESTONLY ( Index *pOld = ) sqlite4HashInsert( &pIndex->pSchema->idxHash, zName, sqlite4Strlen30(zName), 0 ); - assert( db==0 || sqlite4SchemaMutexHeld(db, 0, pIndex->pSchema) ); assert( pOld==pIndex || pOld==0 ); } freeIndex(db, pIndex); } @@ -554,11 +543,10 @@ Db *pDb; assert( db!=0 ); assert( iDb>=0 && iDbnDb ); assert( zTabName ); - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite4HashInsert(&pDb->pSchema->tblHash, zTabName, sqlite4Strlen30(zTabName),0); sqlite4DeleteTable(db, p); @@ -842,11 +830,10 @@ ** then record a pointer to this table in the main database structure ** so that INSERT can find the table easily. */ #ifndef SQLITE_OMIT_AUTOINCREMENT if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); pTable->pSchema->pSeqTab = pTable; } #endif /* Begin generating the code that will insert the table record into @@ -856,37 +843,19 @@ ** indices to be created and the table record must come before the ** indices. Hence, the record number for the table must be allocated ** now. */ if( !db->init.busy && (v = sqlite4GetVdbe(pParse))!=0 ){ - int j1; - int fileFormat; int reg1, reg2, reg3; sqlite4BeginWriteOperation(pParse, 0, iDb); #ifndef SQLITE_OMIT_VIRTUALTABLE if( isVirtual ){ sqlite4VdbeAddOp0(v, OP_VBegin); } #endif - /* If the file format and encoding in the database have not been set, - ** set them now. - */ - reg1 = pParse->regRowid = ++pParse->nMem; - reg2 = pParse->regRoot = ++pParse->nMem; - reg3 = ++pParse->nMem; - sqlite4VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); - sqlite4VdbeUsesBtree(v, iDb); - j1 = sqlite4VdbeAddOp1(v, OP_If, reg3); - fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? - 1 : SQLITE_MAX_FILE_FORMAT; - sqlite4VdbeAddOp2(v, OP_Integer, fileFormat, reg3); - sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3); - sqlite4VdbeAddOp2(v, OP_Integer, ENC(db), reg3); - sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3); - sqlite4VdbeJumpHere(v, j1); /* This just creates a place-holder record in the sqlite_master table. ** The record created does not contain anything yet. It will be replaced ** by the real entry in code generated at sqlite4EndTable(). ** @@ -893,10 +862,13 @@ ** The rowid for the new entry is left in register pParse->regRowid. ** The root page number of the new table is left in reg pParse->regRoot. ** The rowid and root page number values are needed by the code that ** sqlite4EndTable will generate. */ + reg1 = pParse->regRowid = ++pParse->nMem; + reg2 = pParse->regRoot = ++pParse->nMem; + reg3 = ++pParse->nMem; #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ sqlite4VdbeAddOp2(v, OP_Integer, 0, reg2); }else #endif @@ -1303,13 +1275,12 @@ */ void sqlite4ChangeCookie(Parse *pParse, int iDb){ int r1 = sqlite4GetTempReg(pParse); sqlite4 *db = pParse->db; Vdbe *v = pParse->pVdbe; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); sqlite4VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1); - sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); + sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, 0, r1); sqlite4ReleaseTempReg(pParse, r1); } /* ** Measure the number of characters needed to output the given @@ -1606,11 +1577,10 @@ /* Check to see if we need to create an sqlite_sequence table for ** keeping track of autoincrement keys. */ if( p->tabFlags & TF_Autoincrement ){ Db *pDb = &db->aDb[iDb]; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ sqlite4NestedParse(pParse, "CREATE TABLE %Q.sqlite_sequence(name,seq)", pDb->zName ); @@ -1627,11 +1597,10 @@ /* Add the table to the in-memory representation of the database. */ if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); pOld = sqlite4HashInsert(&pSchema->tblHash, p->zName, sqlite4Strlen30(p->zName),p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ db->mallocFailed = 1; @@ -1812,11 +1781,10 @@ pTable->nCol = pSelTab->nCol; pTable->aCol = pSelTab->aCol; pSelTab->nCol = 0; pSelTab->aCol = 0; sqlite4DeleteTable(db, pSelTab); - assert( sqlite4SchemaMutexHeld(db, 0, pTable->pSchema) ); pTable->pSchema->flags |= DB_UnresetViews; }else{ pTable->nCol = 0; nErr++; } @@ -1833,11 +1801,10 @@ /* ** Clear the column names from every VIEW in database idx. */ static void sqliteViewResetAll(sqlite4 *db, int idx){ HashElem *i; - assert( sqlite4SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); if( pTab->pSelect ){ sqliteDeleteColumnNames(db, pTab); @@ -2180,11 +2147,10 @@ } pFKey->isDeferred = 0; pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ - assert( sqlite4SchemaMutexHeld(db, 0, p->pSchema) ); pNextTo = (FKey *)sqlite4HashInsert(&p->pSchema->fkeyHash, pFKey->zTo, sqlite4Strlen30(pFKey->zTo), (void *)pFKey ); if( pNextTo==pFKey ){ db->mallocFailed = 1; @@ -2236,12 +2202,12 @@ ** the index already exists and must be cleared before being refilled and ** the root page number of the index is taken from pIndex->tnum. */ static void sqlite4RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ Table *pTab = pIndex->pTable; /* The table that is indexed */ - int iTab = pParse->nTab++; /* Btree cursor used for pTab */ - int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ + int iTab = pParse->nTab++; /* Cursor used for pTab */ + int iIdx = pParse->nTab++; /* Cursor used for pIndex */ int iSorter; /* Cursor opened by OpenSorter (if in use) */ int addr1; /* Address of top of loop */ int addr2; /* Address to jump to for next iteration */ int tnum; /* Root page of index */ Vdbe *v; /* Generate code into this virtual machine */ @@ -2587,11 +2553,10 @@ pIndex->pTable = pTab; pIndex->nColumn = pList->nExpr; pIndex->onError = (u8)onError; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); /* Check to see if we should honor DESC requests on index columns */ if( pDb->pSchema->file_format>=4 ){ sortOrderMask = -1; /* Honor DESC */ @@ -2717,11 +2682,10 @@ /* Link the new Index structure to its table and to the other ** in-memory database structures. */ if( db->init.busy ){ Index *p; - assert( sqlite4SchemaMutexHeld(db, 0, pIndex->pSchema) ); p = sqlite4HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, sqlite4Strlen30(pIndex->zName), pIndex); if( p ){ assert( p==pIndex ); /* Malloc must have failed */ @@ -3322,20 +3286,19 @@ int i; assert( pParse!=0 ); db = pParse->db; assert( db!=0 ); -/* if( db->aDb[0].pBt==0 ) return; */ if( sqlite4AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){ return; } v = sqlite4GetVdbe(pParse); if( !v ) return; if( type!=TK_DEFERRED ){ for(i=0; inDb; i++){ sqlite4VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); - sqlite4VdbeUsesBtree(v, i); + sqlite4VdbeUsesStorage(v, i); } } sqlite4VdbeAddOp2(v, OP_AutoCommit, 0, 0); } @@ -3397,34 +3360,30 @@ ** Make sure the TEMP database is open and available for use. Return ** the number of errors. Leave any error messages in the pParse structure. */ int sqlite4OpenTempDatabase(Parse *pParse){ sqlite4 *db = pParse->db; - if( db->aDb[1].pBt==0 && !pParse->explain ){ + if( db->aDb[1].pKV==0 && !pParse->explain ){ int rc; - Btree *pBt; +#if 0 static const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; +#endif - rc = sqlite4BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags); + rc = sqlite4KVStoreOpen(db, "temp", ":memory:", &db->aDb[1].pKV, + SQLITE_KVOPEN_TEMPORARY); if( rc!=SQLITE_OK ){ sqlite4ErrorMsg(pParse, "unable to open a temporary database " "file for storing temporary tables"); pParse->rc = rc; return 1; } - sqlite4KVStoreOpen(":memory", &db->aDb[1].pKV); - db->aDb[1].pBt = pBt; assert( db->aDb[1].pSchema ); - if( SQLITE_NOMEM==sqlite4BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ - db->mallocFailed = 1; - return 1; - } } return 0; } /* @@ -3460,13 +3419,12 @@ if( iDb>=0 ){ sqlite4 *db = pToplevel->db; yDbMask mask; assert( iDbnDb ); - assert( db->aDb[iDb].pBt!=0 || iDb==1 ); + assert( db->aDb[iDb].pKV!=0 || iDb==1 ); assert( iDbcookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; if( !OMIT_TEMPDB && iDb==1 ){ @@ -3483,11 +3441,11 @@ void sqlite4CodeVerifyNamedSchema(Parse *pParse, const char *zDb){ sqlite4 *db = pParse->db; int i; for(i=0; inDb; i++){ Db *pDb = &db->aDb[i]; - if( pDb->pBt && (!zDb || 0==sqlite4StrICmp(zDb, pDb->zName)) ){ + if( pDb->pKV && (!zDb || 0==sqlite4StrICmp(zDb, pDb->zName)) ){ sqlite4CodeVerifySchema(pParse, i); } } } @@ -3605,11 +3563,10 @@ int iDb; /* The database index number */ sqlite4 *db = pParse->db; /* The database connection */ HashElem *k; /* For looping over tables in pDb */ Table *pTab; /* A table in the database */ - assert( sqlite4BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ assert( pDb!=0 ); for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ pTab = (Table*)sqliteHashData(k); reindexTable(pParse, pTab, zColl); Index: src/callback.c ================================================================== --- src/callback.c +++ src/callback.c @@ -403,15 +403,14 @@ ** pointer itself, it just cleans up subsidiary resources (i.e. the contents ** of the schema hash tables). ** ** The Schema.cache_size variable is not cleared. */ -void sqlite4SchemaClear(void *p){ +void sqlite4SchemaClear(Schema *pSchema){ Hash temp1; Hash temp2; HashElem *pElem; - Schema *pSchema = (Schema *)p; temp1 = pSchema->tblHash; temp2 = pSchema->trigHash; sqlite4HashInit(&pSchema->trigHash); sqlite4HashClear(&pSchema->idxHash); @@ -435,17 +434,13 @@ /* ** Find and return the schema associated with a BTree. Create ** a new one if necessary. */ -Schema *sqlite4SchemaGet(sqlite4 *db, Btree *pBt){ +Schema *sqlite4SchemaGet(sqlite4 *db){ Schema * p; - if( pBt ){ - p = (Schema *)sqlite4BtreeSchema(pBt, sizeof(Schema), sqlite4SchemaClear); - }else{ - p = (Schema *)sqlite4DbMallocZero(0, sizeof(Schema)); - } + p = (Schema *)sqlite4DbMallocZero(0, sizeof(Schema)); if( !p ){ db->mallocFailed = 1; }else if ( 0==p->file_format ){ sqlite4HashInit(&p->tblHash); sqlite4HashInit(&p->idxHash); Index: src/ctime.c ================================================================== --- src/ctime.c +++ src/ctime.c @@ -237,13 +237,10 @@ "OMIT_FOREIGN_KEY", #endif #ifdef SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif -#ifdef SQLITE_OMIT_INCRBLOB - "OMIT_INCRBLOB", -#endif #ifdef SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif #ifdef SQLITE_OMIT_LIKE_OPTIMIZATION "OMIT_LIKE_OPTIMIZATION", Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1650,11 +1650,10 @@ ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite4VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); - if( rMayHaveNull==0 ) sqlite4VdbeChangeP5(v, BTREE_UNORDERED); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) Index: src/fkey.c ================================================================== --- src/fkey.c +++ src/fkey.c @@ -1182,11 +1182,10 @@ */ void sqlite4FkDelete(sqlite4 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ - assert( db==0 || sqlite4SchemaMutexHeld(db, 0, pTab->pSchema) ); for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ if( !db || db->pnBytesFreed==0 ){ if( pFKey->pPrevTo ){ Index: src/func.c ================================================================== --- src/func.c +++ src/func.c @@ -1192,11 +1192,11 @@ sqlite4_result_text(context, "?000", 4, SQLITE_STATIC); } } #endif /* SQLITE_SOUNDEX */ -#ifndef SQLITE_OMIT_LOAD_EXTENSION +#if 0 /*ndef SQLITE_OMIT_LOAD_EXTENSION*/ /* ** A function that loads a shared-library extension then returns NULL. */ static void loadExt(sqlite4_context *context, int argc, sqlite4_value **argv){ const char *zFile = (const char *)sqlite4_value_text(argv[0]); @@ -1566,11 +1566,11 @@ FUNCTION(replace, 3, 0, 0, replaceFunc ), FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), #endif - #ifndef SQLITE_OMIT_LOAD_EXTENSION + #if 0 /*ndef SQLITE_OMIT_LOAD_EXTENSION*/ FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -235,11 +235,10 @@ assert( v ); /* We failed long ago if this is not so */ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; - assert( sqlite4SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite4OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); sqlite4VdbeAddOp3(v, OP_Null, 0, memId, memId+1); addr = sqlite4VdbeCurrentAddr(v); sqlite4VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); sqlite4VdbeAddOp2(v, OP_Rewind, 0, addr+9); @@ -287,11 +286,10 @@ int j1, j2, j3, j4, j5; int iRec; int memId = p->regCtr; iRec = sqlite4GetTempReg(pParse); - assert( sqlite4SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite4OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite4VdbeAddOp1(v, OP_NotNull, memId+1); j2 = sqlite4VdbeAddOp0(v, OP_Rewind); j3 = sqlite4VdbeAddOp3(v, OP_Column, 0, 0, iRec); j4 = sqlite4VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec); DELETED src/journal.c Index: src/journal.c ================================================================== --- src/journal.c +++ /dev/null @@ -1,238 +0,0 @@ -/* -** 2007 August 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements a special kind of sqlite4_file object used -** by SQLite to create journal files if the atomic-write optimization -** is enabled. -** -** The distinctive characteristic of this sqlite4_file is that the -** actual on disk file is created lazily. When the file is created, -** the caller specifies a buffer size for an in-memory buffer to -** be used to service read() and write() requests. The actual file -** on disk is not created or populated until either: -** -** 1) The in-memory representation grows too large for the allocated -** buffer, or -** 2) The sqlite4JournalCreate() function is called. -*/ -#ifdef SQLITE_ENABLE_ATOMIC_WRITE -#include "sqliteInt.h" - - -/* -** A JournalFile object is a subclass of sqlite4_file used by -** as an open file handle for journal files. -*/ -struct JournalFile { - sqlite4_io_methods *pMethod; /* I/O methods on journal files */ - int nBuf; /* Size of zBuf[] in bytes */ - char *zBuf; /* Space to buffer journal writes */ - int iSize; /* Amount of zBuf[] currently used */ - int flags; /* xOpen flags */ - sqlite4_vfs *pVfs; /* The "real" underlying VFS */ - sqlite4_file *pReal; /* The "real" underlying file descriptor */ - const char *zJournal; /* Name of the journal file */ -}; -typedef struct JournalFile JournalFile; - -/* -** If it does not already exists, create and populate the on-disk file -** for JournalFile p. -*/ -static int createFile(JournalFile *p){ - int rc = SQLITE_OK; - if( !p->pReal ){ - sqlite4_file *pReal = (sqlite4_file *)&p[1]; - rc = sqlite4OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0); - if( rc==SQLITE_OK ){ - p->pReal = pReal; - if( p->iSize>0 ){ - assert(p->iSize<=p->nBuf); - rc = sqlite4OsWrite(p->pReal, p->zBuf, p->iSize, 0); - } - } - } - return rc; -} - -/* -** Close the file. -*/ -static int jrnlClose(sqlite4_file *pJfd){ - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - sqlite4OsClose(p->pReal); - } - sqlite4_free(p->zBuf); - return SQLITE_OK; -} - -/* -** Read data from the file. -*/ -static int jrnlRead( - sqlite4_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite4OsRead(p->pReal, zBuf, iAmt, iOfst); - }else if( (iAmt+iOfst)>p->iSize ){ - rc = SQLITE_IOERR_SHORT_READ; - }else{ - memcpy(zBuf, &p->zBuf[iOfst], iAmt); - } - return rc; -} - -/* -** Write data to the file. -*/ -static int jrnlWrite( - sqlite4_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( !p->pReal && (iOfst+iAmt)>p->nBuf ){ - rc = createFile(p); - } - if( rc==SQLITE_OK ){ - if( p->pReal ){ - rc = sqlite4OsWrite(p->pReal, zBuf, iAmt, iOfst); - }else{ - memcpy(&p->zBuf[iOfst], zBuf, iAmt); - if( p->iSize<(iOfst+iAmt) ){ - p->iSize = (iOfst+iAmt); - } - } - } - return rc; -} - -/* -** Truncate the file. -*/ -static int jrnlTruncate(sqlite4_file *pJfd, sqlite_int64 size){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite4OsTruncate(p->pReal, size); - }else if( sizeiSize ){ - p->iSize = size; - } - return rc; -} - -/* -** Sync the file. -*/ -static int jrnlSync(sqlite4_file *pJfd, int flags){ - int rc; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite4OsSync(p->pReal, flags); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Query the size of the file in bytes. -*/ -static int jrnlFileSize(sqlite4_file *pJfd, sqlite_int64 *pSize){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite4OsFileSize(p->pReal, pSize); - }else{ - *pSize = (sqlite_int64) p->iSize; - } - return rc; -} - -/* -** Table of methods for JournalFile sqlite4_file object. -*/ -static struct sqlite4_io_methods JournalFileMethods = { - 1, /* iVersion */ - jrnlClose, /* xClose */ - jrnlRead, /* xRead */ - jrnlWrite, /* xWrite */ - jrnlTruncate, /* xTruncate */ - jrnlSync, /* xSync */ - jrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnmap */ -}; - -/* -** Open a journal file. -*/ -int sqlite4JournalOpen( - sqlite4_vfs *pVfs, /* The VFS to use for actual file I/O */ - const char *zName, /* Name of the journal file */ - sqlite4_file *pJfd, /* Preallocated, blank file handle */ - int flags, /* Opening flags */ - int nBuf /* Bytes buffered before opening the file */ -){ - JournalFile *p = (JournalFile *)pJfd; - memset(p, 0, sqlite4JournalSize(pVfs)); - if( nBuf>0 ){ - p->zBuf = sqlite4MallocZero(nBuf); - if( !p->zBuf ){ - return SQLITE_NOMEM; - } - }else{ - return sqlite4OsOpen(pVfs, zName, pJfd, flags, 0); - } - p->pMethod = &JournalFileMethods; - p->nBuf = nBuf; - p->flags = flags; - p->zJournal = zName; - p->pVfs = pVfs; - return SQLITE_OK; -} - -/* -** If the argument p points to a JournalFile structure, and the underlying -** file has not yet been created, create it now. -*/ -int sqlite4JournalCreate(sqlite4_file *p){ - if( p->pMethods!=&JournalFileMethods ){ - return SQLITE_OK; - } - return createFile((JournalFile *)p); -} - -/* -** Return the number of bytes required to store a JournalFile that uses vfs -** pVfs to create the underlying on-disk files. -*/ -int sqlite4JournalSize(sqlite4_vfs *pVfs){ - return (pVfs->szOsFile+sizeof(JournalFile)); -} -#endif Index: src/kvmem.c ================================================================== --- src/kvmem.c +++ src/kvmem.c @@ -64,11 +64,11 @@ ** transaction logs is an instance of the following object. */ struct KVMem { KVStore base; /* Base class, must be first */ KVMemNode *pRoot; /* Root of the tree of content */ - int nTrans; /* Number of nested option transactions */ + unsigned openFlags; /* Flags used at open */ KVMemChng **apLog; /* Array of transaction logs */ int nCursor; /* Number of outstanding cursors */ int iMagicKVMemBase; /* Magic number of sanity */ }; #define SQLITE_KVMEMBASE_MAGIC 0xbfcd47d0 @@ -300,18 +300,18 @@ /* ** Create a new change record */ static KVMemChng *kvmemNewChng(KVMem *p, KVMemNode *pNode){ KVMemChng *pChng; - assert( p->nTrans>=2 ); + assert( p->base.iTransLevel>=2 ); pChng = sqlite4_malloc( sizeof(*pChng) ); if( pChng ){ - pChng->pNext = p->apLog[p->nTrans-2]; - p->apLog[p->nTrans-2] = pChng; + pChng->pNext = p->apLog[p->base.iTransLevel-2]; + p->apLog[p->base.iTransLevel-2] = pChng; pChng->pNode = pNode; pChng->oldTrans = pNode->mxTrans; - pNode->mxTrans = p->nTrans; + pNode->mxTrans = p->base.iTransLevel; pChng->pData = pNode->pData; pNode->pData = 0; } return pChng; } @@ -323,14 +323,14 @@ const KVByteArray *aKey, KVSize nKey ){ KVMemNode *pNode; KVMemChng *pChng; - assert( p->nTrans>=2 ); + assert( p->base.iTransLevel>=2 ); pNode = sqlite4_malloc( sizeof(*pNode)+nKey-2 ); if( pNode ){ - memset(pNode, 0, sizeof(*p)); + memset(pNode, 0, sizeof(*pNode)); memcpy(pNode->aKey, aKey, nKey); pNode->nKey = nKey; pNode->nRef = 1; pChng = kvmemNewChng(p, pNode); if( pChng==0 ){ @@ -403,19 +403,19 @@ */ static int kvmemBegin(KVStore *pKVStore, int iLevel){ KVMem *p = (KVMem*)pKVStore; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( iLevel>0 ); - assert( iLevel==2 || iLevel==p->nTrans+1 ); + assert( iLevel==2 || iLevel==p->base.iTransLevel+1 ); if( iLevel>=2 ){ KVMemChng **apNewLog; apNewLog = sqlite4_realloc(p->apLog, sizeof(apNewLog[0])*(iLevel-1) ); if( apNewLog==0 ) return SQLITE_NOMEM; p->apLog = apNewLog; p->apLog[iLevel-2] = 0; } - p->nTrans = iLevel; + p->base.iTransLevel = iLevel; return SQLITE_OK; } /* ** Commit a transaction or subtransaction. @@ -422,22 +422,29 @@ ** ** Make permanent all changes back through the most recent xBegin ** with the iLevel+1. If iLevel==0 then make all changes permanent. ** The argument iLevel will always be less than the current transaction ** level when this routine is called. +** +** Commit is divided into two phases. A rollback is still possible after +** phase one completes. In this implementation, phase one is a no-op since +** phase two cannot fail. ** ** After this routine returns successfully, the transaction level will be ** equal to iLevel. */ -static int kvmemCommit(KVStore *pKVStore, int iLevel){ +static int kvmemCommitPhaseOne(KVStore *pKVStore, int iLevel){ + return SQLITE_OK; +} +static int kvmemCommitPhaseTwo(KVStore *pKVStore, int iLevel){ KVMem *p = (KVMem*)pKVStore; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( iLevel>=0 ); - assert( iLevelnTrans ); - while( p->nTrans>iLevel && p->nTrans>1 ){ + assert( iLevelbase.iTransLevel ); + while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){ KVMemChng *pChng, *pNext; - for(pChng=p->apLog[p->nTrans-2]; pChng; pChng=pNext){ + for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){ KVMemNode *pNode = pChng->pNode; if( pNode->pData ){ pNode->mxTrans = pChng->oldTrans; }else{ kvmemRemoveNode(p, pNode); @@ -444,14 +451,14 @@ } kvmemDataUnref(pChng->pData); pNext = pChng->pNext; sqlite4_free(pChng); } - p->apLog[p->nTrans-2] = 0; - p->nTrans--; + p->apLog[p->base.iTransLevel-2] = 0; + p->base.iTransLevel--; } - p->nTrans = iLevel; + p->base.iTransLevel = iLevel; return SQLITE_OK; } /* ** Rollback a transaction or subtransaction. @@ -465,14 +472,14 @@ */ static int kvmemRollback(KVStore *pKVStore, int iLevel){ KVMem *p = (KVMem*)pKVStore; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( iLevel>=0 ); - assert( iLevelnTrans ); - while( p->nTrans>iLevel && p->nTrans>1 ){ + assert( iLevelbase.iTransLevel ); + while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){ KVMemChng *pChng, *pNext; - for(pChng=p->apLog[p->nTrans-2]; pChng; pChng=pNext){ + for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){ KVMemNode *pNode = pChng->pNode; if( pChng->pData ){ kvmemDataUnref(pNode->pData); pNode->pData = pChng->pData; pNode->mxTrans = pChng->oldTrans; @@ -480,17 +487,27 @@ kvmemRemoveNode(p, pNode); } pNext = pChng->pNext; sqlite4_free(pChng); } - p->apLog[p->nTrans-2] = 0; - p->nTrans--; + p->apLog[p->base.iTransLevel-2] = 0; + p->base.iTransLevel--; } - p->nTrans = iLevel; + p->base.iTransLevel = iLevel; return SQLITE_OK; } +/* +** Revert a transaction back to what it was when it started. +*/ +static int kvmemRevert(KVStore *pKVStore, int iLevel){ + int rc = kvmemRollback(pKVStore, iLevel-1); + if( rc==SQLITE_OK ){ + rc = kvmemBegin(pKVStore, iLevel); + } + return rc; +} /* ** Implementation of the xReplace(X, aKey, nKey, aData, nData) method. ** ** Insert or replace the entry with the key aKey[0..nKey-1]. The data for @@ -511,11 +528,11 @@ KVMem *p = (KVMem*)pKVStore; KVMemNode *pNew, *pNode; KVMemData *pData; KVMemChng *pChng; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); - assert( p->nTrans>=2 ); + assert( p->base.iTransLevel>=2 ); pData = kvmemDataNew(aData, nData); if( pData==0 ) return SQLITE_NOMEM; if( p->pRoot==0 ){ pNode = pNew = kvmemNewNode(p, aKey, nKey); if( pNew==0 ) goto KVMemReplace_nomem; @@ -541,11 +558,11 @@ if( pNew==0 ) goto KVMemReplace_nomem; pNew->pUp = pNode; break; } }else{ - if( pNode->mxTrans==p->nTrans ){ + if( pNode->mxTrans==p->base.iTransLevel ){ kvmemDataUnref(pNode->pData); }else{ pChng = kvmemNewChng(p, pNode); if( pChng==0 ) goto KVMemReplace_nomem; } @@ -624,30 +641,49 @@ KVSize nKey, int direction ){ KVMemCursor *pCur; KVMemNode *pNode; + KVMemNode *pBest = 0; int c; + int rc = SQLITE_NOTFOUND; kvmemReset(pKVCursor); pCur = (KVMemCursor*)pKVCursor; assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); pNode = pCur->pOwner->pRoot; while( pNode ){ c = kvmemKeyCompare(aKey, nKey, pNode->aKey, pNode->nKey); - if( c==0 - || (c<0 && pNode->pBefore==0 && direction>0) - || (c>0 && pNode->pAfter==0 && direction<0) - ){ - pCur->pNode = kvmemNodeRef(pNode); - pCur->pData = kvmemDataRef(pNode->pData); - return c==0 ? SQLITE_OK : SQLITE_INEXACT; - } - pNode = (c<0) ? pNode->pBefore : pNode->pAfter; - } - return SQLITE_NOTFOUND; + if( c==0 ){ + pBest = pNode; + rc = SQLITE_OK; + pNode = 0; + }else if( c>0 ){ + if( direction<0 ){ + pBest = pNode; + rc = SQLITE_INEXACT; + } + pNode = pNode->pAfter; + }else{ + if( direction>0 ){ + pBest = pNode; + rc = SQLITE_INEXACT; + } + pNode = pNode->pBefore; + } + } + kvmemNodeUnref(pCur->pNode); + kvmemDataUnref(pCur->pData); + if( pBest ){ + pCur->pNode = kvmemNodeRef(pBest); + pCur->pData = kvmemDataRef(pBest->pData); + }else{ + pCur->pNode = 0; + pCur->pData = 0; + } + return rc; } /* ** Delete the entry that the cursor is pointing to. ** @@ -664,15 +700,15 @@ pCur = (KVMemCursor*)pKVCursor; assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); p = pCur->pOwner; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); - assert( p->nTrans>=2 ); + assert( p->base.iTransLevel>=2 ); pNode = pCur->pNode; if( pNode==0 ) return SQLITE_OK; if( pNode->pData==0 ) return SQLITE_OK; - if( pNode->mxTransnTrans ){ + if( pNode->mxTransbase.iTransLevel ){ pChng = kvmemNewChng(p, pNode); if( pChng==0 ) return SQLITE_NOMEM; }else{ kvmemDataUnref(pNode->pData); pNode->pData = 0; @@ -696,11 +732,11 @@ }while( pNode && pNode->pData==0 ); if( pNode ){ pCur->pNode = kvmemNodeRef(pNode); pCur->pData = kvmemDataRef(pNode->pData); } - return pNode ? SQLITE_OK : SQLITE_DONE; + return pNode ? SQLITE_OK : SQLITE_NOTFOUND; } /* ** Move a cursor to the previous non-deleted node. */ @@ -717,11 +753,11 @@ }while( pNode && pNode->pData==0 ); if( pNode ){ pCur->pNode = kvmemNodeRef(pNode); pCur->pData = kvmemDataRef(pNode->pData); } - return pNode ? SQLITE_OK : SQLITE_DONE; + return pNode ? SQLITE_OK : SQLITE_NOTFOUND; } /* ** Return the key of the node the cursor is pointing to. */ @@ -778,11 +814,14 @@ static int kvmemClose(KVStore *pKVStore){ KVMem *p = (KVMem*)pKVStore; if( p==0 ) return SQLITE_OK; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( p->nCursor==0 ); - if( p->nTrans ) kvmemCommit(pKVStore, 0); + if( p->base.iTransLevel ){ + kvmemCommitPhaseOne(pKVStore, 0); + kvmemCommitPhaseTwo(pKVStore, 0); + } sqlite4_free(p->apLog); kvmemClearTree(p->pRoot); memset(p, 0, sizeof(*p)); sqlite4_free(p); return SQLITE_OK; @@ -799,22 +838,25 @@ kvmemKey, kvmemData, kvmemReset, kvmemCloseCursor, kvmemBegin, - kvmemCommit, + kvmemCommitPhaseOne, + kvmemCommitPhaseTwo, kvmemRollback, + kvmemRevert, kvmemClose }; /* ** Create a new in-memory storage engine and return a pointer to it. */ -int sqlite4KVStoreOpenMem(KVStore **ppKVStore){ +int sqlite4KVStoreOpenMem(KVStore **ppKVStore, unsigned openFlags){ KVMem *pNew = sqlite4_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->base.pStoreVfunc = &kvmemMethods; pNew->iMagicKVMemBase = SQLITE_KVMEMBASE_MAGIC; + pNew->openFlags = openFlags; *ppKVStore = (KVStore*)pNew; return SQLITE_OK; } Index: src/legacy.c ================================================================== --- src/legacy.c +++ src/legacy.c @@ -68,13 +68,11 @@ while( 1 ){ int i; rc = sqlite4_step(pStmt); /* Invoke the callback function if required */ - if( xCallback && (SQLITE_ROW==rc || - (SQLITE_DONE==rc && !callbackIsInit - && db->flags&SQLITE_NullCallback)) ){ + if( xCallback && SQLITE_ROW==rc ){ if( !callbackIsInit ){ azCols = sqlite4DbMallocZero(db, 2*nCol*sizeof(const char*) + 1); if( azCols==0 ){ goto exec_out; } DELETED src/loadext.c Index: src/loadext.c ================================================================== --- src/loadext.c +++ /dev/null @@ -1,655 +0,0 @@ -/* -** 2006 June 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to dynamically load extensions into -** the SQLite library. -*/ - -#ifndef SQLITE_CORE - #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite4ext.h */ -#endif -#include "sqlite4ext.h" -#include "sqliteInt.h" -#include - -#ifndef SQLITE_OMIT_LOAD_EXTENSION - -/* -** Some API routines are omitted when various features are -** excluded from a build of SQLite. Substitute a NULL pointer -** for any missing APIs. -*/ -#ifndef SQLITE_ENABLE_COLUMN_METADATA -# define sqlite4_column_database_name 0 -# define sqlite4_column_database_name16 0 -# define sqlite4_column_table_name 0 -# define sqlite4_column_table_name16 0 -# define sqlite4_column_origin_name 0 -# define sqlite4_column_origin_name16 0 -# define sqlite4_table_column_metadata 0 -#endif - -#ifdef SQLITE_OMIT_AUTHORIZATION -# define sqlite4_set_authorizer 0 -#endif - -#ifdef SQLITE_OMIT_UTF16 -# define sqlite4_bind_text16 0 -# define sqlite4_collation_needed16 0 -# define sqlite4_column_decltype16 0 -# define sqlite4_column_name16 0 -# define sqlite4_column_text16 0 -# define sqlite4_complete16 0 -# define sqlite4_create_function16 0 -# define sqlite4_errmsg16 0 -# define sqlite4_open16 0 -# define sqlite4_prepare16 0 -# define sqlite4_prepare16_v2 0 -# define sqlite4_result_error16 0 -# define sqlite4_result_text16 0 -# define sqlite4_result_text16be 0 -# define sqlite4_result_text16le 0 -# define sqlite4_value_text16 0 -# define sqlite4_value_text16be 0 -# define sqlite4_value_text16le 0 -# define sqlite4_column_database_name16 0 -# define sqlite4_column_table_name16 0 -# define sqlite4_column_origin_name16 0 -#endif - -#ifdef SQLITE_OMIT_COMPLETE -# define sqlite4_complete 0 -# define sqlite4_complete16 0 -#endif - -#ifdef SQLITE_OMIT_DECLTYPE -# define sqlite4_column_decltype16 0 -# define sqlite4_column_decltype 0 -#endif - -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK -# define sqlite4_progress_handler 0 -#endif - -#ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite4_create_module 0 -# define sqlite4_create_module_v2 0 -# define sqlite4_declare_vtab 0 -# define sqlite4_vtab_config 0 -# define sqlite4_vtab_on_conflict 0 -#endif - -#ifdef SQLITE_OMIT_SHARED_CACHE -# define sqlite4_enable_shared_cache 0 -#endif - -#ifdef SQLITE_OMIT_TRACE -# define sqlite4_profile 0 -# define sqlite4_trace 0 -#endif - -#ifdef SQLITE_OMIT_GET_TABLE -# define sqlite4_free_table 0 -# define sqlite4_get_table 0 -#endif - -#ifdef SQLITE_OMIT_INCRBLOB -#define sqlite4_bind_zeroblob 0 -#define sqlite4_blob_bytes 0 -#define sqlite4_blob_close 0 -#define sqlite4_blob_open 0 -#define sqlite4_blob_read 0 -#define sqlite4_blob_write 0 -#define sqlite4_blob_reopen 0 -#endif - -/* -** The following structure contains pointers to all SQLite API routines. -** A pointer to this structure is passed into extensions when they are -** loaded so that the extension can make calls back into the SQLite -** library. -** -** When adding new APIs, add them to the bottom of this structure -** in order to preserve backwards compatibility. -** -** Extensions that use newer APIs should first call the -** sqlite4_libversion_number() to make sure that the API they -** intend to use is supported by the library. Extensions should -** also check to make sure that the pointer to the function is -** not NULL before calling it. -*/ -static const sqlite4_api_routines sqlite4Apis = { - sqlite4_aggregate_context, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_aggregate_count, -#else - 0, -#endif - sqlite4_bind_blob, - sqlite4_bind_double, - sqlite4_bind_int, - sqlite4_bind_int64, - sqlite4_bind_null, - sqlite4_bind_parameter_count, - sqlite4_bind_parameter_index, - sqlite4_bind_parameter_name, - sqlite4_bind_text, - sqlite4_bind_text16, - sqlite4_bind_value, - sqlite4_busy_handler, - sqlite4_busy_timeout, - sqlite4_changes, - sqlite4_close, - sqlite4_collation_needed, - sqlite4_collation_needed16, - sqlite4_column_blob, - sqlite4_column_bytes, - sqlite4_column_bytes16, - sqlite4_column_count, - sqlite4_column_database_name, - sqlite4_column_database_name16, - sqlite4_column_decltype, - sqlite4_column_decltype16, - sqlite4_column_double, - sqlite4_column_int, - sqlite4_column_int64, - sqlite4_column_name, - sqlite4_column_name16, - sqlite4_column_origin_name, - sqlite4_column_origin_name16, - sqlite4_column_table_name, - sqlite4_column_table_name16, - sqlite4_column_text, - sqlite4_column_text16, - sqlite4_column_type, - sqlite4_column_value, - sqlite4_commit_hook, - sqlite4_complete, - sqlite4_complete16, - sqlite4_create_collation, - sqlite4_create_function, - sqlite4_create_function16, - sqlite4_create_module, - sqlite4_data_count, - sqlite4_db_handle, - sqlite4_declare_vtab, - sqlite4_enable_shared_cache, - sqlite4_errcode, - sqlite4_errmsg, - sqlite4_errmsg16, - sqlite4_exec, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_expired, -#else - 0, -#endif - sqlite4_finalize, - sqlite4_free, - sqlite4_free_table, - sqlite4_get_autocommit, - sqlite4_get_auxdata, - sqlite4_get_table, - 0, /* Was sqlite4_global_recover(), but that function is deprecated */ - sqlite4_interrupt, - sqlite4_last_insert_rowid, - sqlite4_libversion, - sqlite4_libversion_number, - sqlite4_malloc, - sqlite4_mprintf, - sqlite4_open, - sqlite4_open16, - sqlite4_prepare, - sqlite4_prepare16, - sqlite4_profile, - sqlite4_progress_handler, - sqlite4_realloc, - sqlite4_reset, - sqlite4_result_blob, - sqlite4_result_double, - sqlite4_result_error, - sqlite4_result_error16, - sqlite4_result_int, - sqlite4_result_int64, - sqlite4_result_null, - sqlite4_result_text, - sqlite4_result_text16, - sqlite4_result_text16be, - sqlite4_result_text16le, - sqlite4_result_value, - sqlite4_rollback_hook, - sqlite4_set_authorizer, - sqlite4_set_auxdata, - sqlite4_snprintf, - sqlite4_step, - sqlite4_table_column_metadata, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_thread_cleanup, -#else - 0, -#endif - sqlite4_total_changes, - sqlite4_trace, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_transfer_bindings, -#else - 0, -#endif - sqlite4_update_hook, - sqlite4_user_data, - sqlite4_value_blob, - sqlite4_value_bytes, - sqlite4_value_bytes16, - sqlite4_value_double, - sqlite4_value_int, - sqlite4_value_int64, - sqlite4_value_numeric_type, - sqlite4_value_text, - sqlite4_value_text16, - sqlite4_value_text16be, - sqlite4_value_text16le, - sqlite4_value_type, - sqlite4_vmprintf, - /* - ** The original API set ends here. All extensions can call any - ** of the APIs above provided that the pointer is not NULL. But - ** before calling APIs that follow, extension should check the - ** sqlite4_libversion_number() to make sure they are dealing with - ** a library that is new enough to support that API. - ************************************************************************* - */ - sqlite4_overload_function, - - /* - ** Added after 3.3.13 - */ - sqlite4_prepare_v2, - sqlite4_prepare16_v2, - sqlite4_clear_bindings, - - /* - ** Added for 3.4.1 - */ - sqlite4_create_module_v2, - - /* - ** Added for 3.5.0 - */ - sqlite4_bind_zeroblob, - sqlite4_blob_bytes, - sqlite4_blob_close, - sqlite4_blob_open, - sqlite4_blob_read, - sqlite4_blob_write, - sqlite4_file_control, - sqlite4_memory_highwater, - sqlite4_memory_used, -#ifdef SQLITE_MUTEX_OMIT - 0, - 0, - 0, - 0, - 0, -#else - sqlite4_mutex_alloc, - sqlite4_mutex_enter, - sqlite4_mutex_free, - sqlite4_mutex_leave, - sqlite4_mutex_try, -#endif - sqlite4_open_v2, - sqlite4_release_memory, - sqlite4_result_error_nomem, - sqlite4_result_error_toobig, - sqlite4_sleep, - sqlite4_soft_heap_limit, - sqlite4_vfs_find, - sqlite4_vfs_register, - sqlite4_vfs_unregister, - - /* - ** Added for 3.5.8 - */ - sqlite4_threadsafe, - sqlite4_result_zeroblob, - sqlite4_result_error_code, - sqlite4_test_control, - sqlite4_randomness, - sqlite4_context_db_handle, - - /* - ** Added for 3.6.0 - */ - sqlite4_extended_result_codes, - sqlite4_limit, - sqlite4_next_stmt, - sqlite4_sql, - sqlite4_status, - - /* - ** Added for 3.7.4 - */ - sqlite4_backup_finish, - sqlite4_backup_init, - sqlite4_backup_pagecount, - sqlite4_backup_remaining, - sqlite4_backup_step, -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - sqlite4_compileoption_get, - sqlite4_compileoption_used, -#else - 0, - 0, -#endif - sqlite4_create_function_v2, - sqlite4_db_config, - sqlite4_db_mutex, - sqlite4_db_status, - sqlite4_extended_errcode, - sqlite4_log, - sqlite4_soft_heap_limit64, - sqlite4_sourceid, - sqlite4_stmt_status, - sqlite4_strnicmp, -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - sqlite4_unlock_notify, -#else - 0, -#endif -#ifndef SQLITE_OMIT_WAL - sqlite4_wal_autocheckpoint, - sqlite4_wal_checkpoint, - sqlite4_wal_hook, -#else - 0, - 0, - 0, -#endif - sqlite4_blob_reopen, - sqlite4_vtab_config, - sqlite4_vtab_on_conflict, -}; - -/* -** Attempt to load an SQLite extension library contained in the file -** zFile. The entry point is zProc. zProc may be 0 in which case a -** default entry point name (sqlite4_extension_init) is used. Use -** of the default name is recommended. -** -** Return SQLITE_OK on success and SQLITE_ERROR if something goes wrong. -** -** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with -** error message text. The calling function should free this memory -** by calling sqlite4DbFree(db, ). -*/ -static int sqlite4LoadExtension( - sqlite4 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Use "sqlite4_extension_init" if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -){ - sqlite4_vfs *pVfs = db->pVfs; - void *handle; - int (*xInit)(sqlite4*,char**,const sqlite4_api_routines*); - char *zErrmsg = 0; - void **aHandle; - int nMsg = 300 + sqlite4Strlen30(zFile); - - if( pzErrMsg ) *pzErrMsg = 0; - - /* Ticket #1863. To avoid a creating security problems for older - ** applications that relink against newer versions of SQLite, the - ** ability to run load_extension is turned off by default. One - ** must call sqlite4_enable_load_extension() to turn on extension - ** loading. Otherwise you get the following error. - */ - if( (db->flags & SQLITE_LoadExtension)==0 ){ - if( pzErrMsg ){ - *pzErrMsg = sqlite4_mprintf("not authorized"); - } - return SQLITE_ERROR; - } - - if( zProc==0 ){ - zProc = "sqlite4_extension_init"; - } - - handle = sqlite4OsDlOpen(pVfs, zFile); - if( handle==0 ){ - if( pzErrMsg ){ - *pzErrMsg = zErrmsg = sqlite4_malloc(nMsg); - if( zErrmsg ){ - sqlite4_snprintf(nMsg, zErrmsg, - "unable to open shared library [%s]", zFile); - sqlite4OsDlError(pVfs, nMsg-1, zErrmsg); - } - } - return SQLITE_ERROR; - } - xInit = (int(*)(sqlite4*,char**,const sqlite4_api_routines*)) - sqlite4OsDlSym(pVfs, handle, zProc); - if( xInit==0 ){ - if( pzErrMsg ){ - nMsg += sqlite4Strlen30(zProc); - *pzErrMsg = zErrmsg = sqlite4_malloc(nMsg); - if( zErrmsg ){ - sqlite4_snprintf(nMsg, zErrmsg, - "no entry point [%s] in shared library [%s]", zProc,zFile); - sqlite4OsDlError(pVfs, nMsg-1, zErrmsg); - } - sqlite4OsDlClose(pVfs, handle); - } - return SQLITE_ERROR; - }else if( xInit(db, &zErrmsg, &sqlite4Apis) ){ - if( pzErrMsg ){ - *pzErrMsg = sqlite4_mprintf("error during initialization: %s", zErrmsg); - } - sqlite4_free(zErrmsg); - sqlite4OsDlClose(pVfs, handle); - return SQLITE_ERROR; - } - - /* Append the new shared library handle to the db->aExtension array. */ - aHandle = sqlite4DbMallocZero(db, sizeof(handle)*(db->nExtension+1)); - if( aHandle==0 ){ - return SQLITE_NOMEM; - } - if( db->nExtension>0 ){ - memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension); - } - sqlite4DbFree(db, db->aExtension); - db->aExtension = aHandle; - - db->aExtension[db->nExtension++] = handle; - return SQLITE_OK; -} -int sqlite4_load_extension( - sqlite4 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Use "sqlite4_extension_init" if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -){ - int rc; - sqlite4_mutex_enter(db->mutex); - rc = sqlite4LoadExtension(db, zFile, zProc, pzErrMsg); - rc = sqlite4ApiExit(db, rc); - sqlite4_mutex_leave(db->mutex); - return rc; -} - -/* -** Call this routine when the database connection is closing in order -** to clean up loaded extensions -*/ -void sqlite4CloseExtensions(sqlite4 *db){ - int i; - assert( sqlite4_mutex_held(db->mutex) ); - for(i=0; inExtension; i++){ - sqlite4OsDlClose(db->pVfs, db->aExtension[i]); - } - sqlite4DbFree(db, db->aExtension); -} - -/* -** Enable or disable extension loading. Extension loading is disabled by -** default so as not to open security holes in older applications. -*/ -int sqlite4_enable_load_extension(sqlite4 *db, int onoff){ - sqlite4_mutex_enter(db->mutex); - if( onoff ){ - db->flags |= SQLITE_LoadExtension; - }else{ - db->flags &= ~SQLITE_LoadExtension; - } - sqlite4_mutex_leave(db->mutex); - return SQLITE_OK; -} - -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ - -/* -** The auto-extension code added regardless of whether or not extension -** loading is supported. We need a dummy sqlite4Apis pointer for that -** code if regular extension loading is not available. This is that -** dummy pointer. -*/ -#ifdef SQLITE_OMIT_LOAD_EXTENSION -static const sqlite4_api_routines sqlite4Apis = { 0 }; -#endif - - -/* -** The following object holds the list of automatically loaded -** extensions. -** -** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER -** mutex must be held while accessing this list. -*/ -typedef struct sqlite4AutoExtList sqlite4AutoExtList; -static SQLITE_WSD struct sqlite4AutoExtList { - int nExt; /* Number of entries in aExt[] */ - void (**aExt)(void); /* Pointers to the extension init functions */ -} sqlite4Autoext = { 0, 0 }; - -/* The "wsdAutoext" macro will resolve to the autoextension -** state vector. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdStat can refer directly -** to the "sqlite4Autoext" state vector declared above. -*/ -#ifdef SQLITE_OMIT_WSD -# define wsdAutoextInit \ - sqlite4AutoExtList *x = &GLOBAL(sqlite4AutoExtList,sqlite4Autoext) -# define wsdAutoext x[0] -#else -# define wsdAutoextInit -# define wsdAutoext sqlite4Autoext -#endif - - -/* -** Register a statically linked extension that is automatically -** loaded by every new database connection. -*/ -int sqlite4_auto_extension(void (*xInit)(void)){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite4_initialize(); - if( rc ){ - return rc; - }else -#endif - { - int i; -#if SQLITE_THREADSAFE - sqlite4_mutex *mutex = sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); -#endif - wsdAutoextInit; - sqlite4_mutex_enter(mutex); - for(i=0; i=wsdAutoext.nExt ){ - xInit = 0; - go = 0; - }else{ - xInit = (int(*)(sqlite4*,char**,const sqlite4_api_routines*)) - wsdAutoext.aExt[i]; - } - sqlite4_mutex_leave(mutex); - zErrmsg = 0; - if( xInit && (rc = xInit(db, &zErrmsg, &sqlite4Apis))!=0 ){ - sqlite4Error(db, rc, - "automatic extension loading failed: %s", zErrmsg); - go = 0; - } - sqlite4_free(zErrmsg); - } -} Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -171,36 +171,18 @@ /* Do the rest of the initialization under the recursive mutex so ** that we will be able to handle recursive calls into ** sqlite4_initialize(). The recursive calls normally come through ** sqlite4_os_init() when it invokes sqlite4_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 sqlite4_pcache_methods.xInit() all is embedded in the - ** call to sqlite4PcacheInitialize(). */ sqlite4_mutex_enter(sqlite4GlobalConfig.pInitMutex); if( sqlite4GlobalConfig.isInit==0 && sqlite4GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite4GlobalFunctions); sqlite4GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite4GlobalFunctions)); sqlite4RegisterGlobalFunctions(); - if( sqlite4GlobalConfig.isPCacheInit==0 ){ - rc = sqlite4PcacheInitialize(); - } - if( rc==SQLITE_OK ){ - sqlite4GlobalConfig.isPCacheInit = 1; - rc = sqlite4OsInit(); - } - if( rc==SQLITE_OK ){ - sqlite4PCacheBufferSetup( sqlite4GlobalConfig.pPage, - sqlite4GlobalConfig.szPage, sqlite4GlobalConfig.nPage); - sqlite4GlobalConfig.isInit = 1; - } + rc = sqlite4OsInit(); sqlite4GlobalConfig.inProgress = 0; } sqlite4_mutex_leave(sqlite4GlobalConfig.pInitMutex); /* Go back under the static mutex and clean up the recursive @@ -260,17 +242,13 @@ #ifdef SQLITE_EXTRA_SHUTDOWN void SQLITE_EXTRA_SHUTDOWN(void); SQLITE_EXTRA_SHUTDOWN(); #endif sqlite4_os_end(); - sqlite4_reset_auto_extension(); + /* sqlite4_reset_auto_extension(); */ sqlite4GlobalConfig.isInit = 0; } - if( sqlite4GlobalConfig.isPCacheInit ){ - sqlite4PcacheShutdown(); - sqlite4GlobalConfig.isPCacheInit = 0; - } if( sqlite4GlobalConfig.isMallocInit ){ sqlite4MallocEnd(); sqlite4GlobalConfig.isMallocInit = 0; } if( sqlite4GlobalConfig.isMutexInit ){ @@ -358,40 +336,10 @@ sqlite4GlobalConfig.pScratch = va_arg(ap, void*); sqlite4GlobalConfig.szScratch = va_arg(ap, int); sqlite4GlobalConfig.nScratch = va_arg(ap, int); break; } - case SQLITE_CONFIG_PAGECACHE: { - /* Designate a buffer for page cache memory space */ - sqlite4GlobalConfig.pPage = va_arg(ap, void*); - sqlite4GlobalConfig.szPage = va_arg(ap, int); - sqlite4GlobalConfig.nPage = va_arg(ap, int); - break; - } - - case SQLITE_CONFIG_PCACHE: { - /* no-op */ - break; - } - case SQLITE_CONFIG_GETPCACHE: { - /* now an error */ - rc = SQLITE_ERROR; - break; - } - - case SQLITE_CONFIG_PCACHE2: { - /* Specify an alternative page cache implementation */ - sqlite4GlobalConfig.pcache2 = *va_arg(ap, sqlite4_pcache_methods2*); - break; - } - case SQLITE_CONFIG_GETPCACHE2: { - if( sqlite4GlobalConfig.pcache2.xInit==0 ){ - sqlite4PCacheSetDefault(); - } - *va_arg(ap, sqlite4_pcache_methods2*) = sqlite4GlobalConfig.pcache2; - break; - } #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) case SQLITE_CONFIG_HEAP: { /* Designate a buffer for heap memory space */ sqlite4GlobalConfig.pHeap = va_arg(ap, void*); @@ -537,21 +485,11 @@ /* ** Free up as much memory as we can from the given database ** connection. */ int sqlite4_db_release_memory(sqlite4 *db){ - int i; sqlite4_mutex_enter(db->mutex); - sqlite4BtreeEnterAll(db); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - Pager *pPager = sqlite4BtreePager(pBt); - sqlite4PagerShrink(pPager); - } - } - sqlite4BtreeLeaveAll(db); sqlite4_mutex_leave(db->mutex); return SQLITE_OK; } /* @@ -754,28 +692,16 @@ sqlite4_mutex_leave(db->mutex); return SQLITE_BUSY; } assert( sqlite4SafetyCheckSickOrOk(db) ); - for(j=0; jnDb; j++){ - Btree *pBt = db->aDb[j].pBt; - if( pBt && sqlite4BtreeIsInBackup(pBt) ){ - sqlite4Error(db, SQLITE_BUSY, - "unable to close due to unfinished backup operation"); - sqlite4_mutex_leave(db->mutex); - return SQLITE_BUSY; - } - } - /* Free any outstanding Savepoint structures. */ sqlite4CloseSavepoints(db); for(j=0; jnDb; j++){ struct Db *pDb = &db->aDb[j]; - if( pDb->pBt ){ - sqlite4BtreeClose(pDb->pBt); - pDb->pBt = 0; + if( pDb->pKV ){ sqlite4KVStoreClose(pDb->pKV); pDb->pKV = 0; if( j!=1 ){ pDb->pSchema = 0; } @@ -826,16 +752,15 @@ sqlite4Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ if( db->pErr ){ sqlite4ValueFree(db->pErr); } - sqlite4CloseExtensions(db); db->magic = SQLITE_MAGIC_ERROR; /* The temp-database schema is allocated differently from the other schema - ** objects (using sqliteMalloc() directly, instead of sqlite4BtreeSchema()). + ** objects (using sqliteMalloc() directly, instead of sqlite4BTreeSchema()). ** So it needs to be freed here. Todo: Why not roll the temp schema into ** the same sqliteMalloc() as the one that allocates the database ** structure? */ sqlite4DbFree(db, db->aDb[1].pSchema); @@ -857,15 +782,15 @@ int i; int inTrans = 0; assert( sqlite4_mutex_held(db->mutex) ); sqlite4BeginBenignMalloc(); for(i=0; inDb; i++){ - if( db->aDb[i].pBt ){ - if( sqlite4BtreeIsInTrans(db->aDb[i].pBt) ){ + if( db->aDb[i].pKV ){ + if( db->aDb[i].pKV->iTransLevel ){ inTrans = 1; } - sqlite4BtreeRollback(db->aDb[i].pBt); + sqlite4KVStoreRollback(db->aDb[i].pKV, 0); db->aDb[i].inTrans = 0; } } sqlite4VtabRollback(db); sqlite4EndBenignMalloc(); @@ -1478,55 +1403,10 @@ */ int sqlite4_wal_checkpoint(sqlite4 *db, const char *zDb){ return sqlite4_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0); } -#ifndef SQLITE_OMIT_WAL -/* -** Run a checkpoint on database iDb. This is a no-op if database iDb is -** not currently open in WAL mode. -** -** If a transaction is open on the database being checkpointed, this -** function returns SQLITE_LOCKED and a checkpoint is not attempted. If -** an error occurs while running the checkpoint, an SQLite error code is -** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK. -** -** The mutex on database handle db should be held by the caller. The mutex -** associated with the specific b-tree being checkpointed is taken by -** this function while the checkpoint is running. -** -** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are -** checkpointed. If an error is encountered it is returned immediately - -** no attempt is made to checkpoint any remaining databases. -** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. -*/ -int sqlite4Checkpoint(sqlite4 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; /* Return code */ - int i; /* Used to iterate through attached dbs */ - int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ - - assert( sqlite4_mutex_held(db->mutex) ); - assert( !pnLog || *pnLog==-1 ); - assert( !pnCkpt || *pnCkpt==-1 ); - - for(i=0; inDb && rc==SQLITE_OK; i++){ - if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ - rc = sqlite4BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); - pnLog = 0; - pnCkpt = 0; - if( rc==SQLITE_BUSY ){ - bBusy = 1; - rc = SQLITE_OK; - } - } - } - - return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc; -} -#endif /* SQLITE_OMIT_WAL */ - /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime ** parameter) and the compile time value of SQLITE_TEMP_STORE. The @@ -1663,11 +1543,11 @@ sqlite4* db, const char *zName, u8 enc, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*), - int(*xMakeKey)(void*,int,const void*,int,const void*), + int(*xMakeKey)(void*,const void*,int,const void*,int), void(*xDel)(void*) ){ CollSeq *pColl; int enc2; int nName = sqlite4Strlen30(zName); @@ -2077,11 +1957,11 @@ const char *zVfs /* Name of the VFS to use */ ){ sqlite4 *db; /* Store allocated handle here */ int rc; /* Return code */ int isThreadsafe; /* True for threadsafe connections */ - char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ + char *zOpen = 0; /* Filename passed to StorageOpen() */ char *zErrMsg = 0; /* Error message from sqlite4ParseUri() */ *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite4_initialize(); @@ -2164,11 +2044,11 @@ assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; - db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger + db->flags |= SQLITE_AutoIndex | SQLITE_EnableTrigger #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension @@ -2211,30 +2091,26 @@ sqlite4_free(zErrMsg); goto opendb_out; } /* Open the backend database driver */ - rc = sqlite4BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, - flags | SQLITE_OPEN_MAIN_DB); + rc = sqlite4KVStoreOpen(db, "main", zOpen, &db->aDb[0].pKV, 0); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite4Error(db, rc, 0); goto opendb_out; } - db->aDb[0].pSchema = sqlite4SchemaGet(db, db->aDb[0].pBt); - db->aDb[1].pSchema = sqlite4SchemaGet(db, 0); - sqlite4KVStoreOpen(zOpen, &db->aDb[0].pKV); + db->aDb[0].pSchema = sqlite4SchemaGet(db); + db->aDb[1].pSchema = sqlite4SchemaGet(db); /* The default safety_level for the main database is 'full'; for the temp ** database it is 'NONE'. This matches the pager layer defaults. */ db->aDb[0].zName = "main"; - db->aDb[0].safety_level = 3; db->aDb[1].zName = "temp"; - db->aDb[1].safety_level = 1; db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ goto opendb_out; } @@ -2249,11 +2125,11 @@ /* Load automatic extensions - extensions that have been registered ** using the sqlite4_automatic_extension() API. */ rc = sqlite4_errcode(db); if( rc==SQLITE_OK ){ - sqlite4AutoLoadExtensions(db); + /* sqlite4AutoLoadExtensions(db); */ rc = sqlite4_errcode(db); if( rc!=SQLITE_OK ){ goto opendb_out; } } @@ -2290,20 +2166,10 @@ } #endif sqlite4Error(db, rc, 0); - /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking - ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking - ** mode. Doing nothing at all also makes NORMAL the default. - */ -#ifdef SQLITE_DEFAULT_LOCKING_MODE - db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; - sqlite4PagerLockingMode(sqlite4BtreePager(db->aDb[0].pBt), - SQLITE_DEFAULT_LOCKING_MODE); -#endif - /* Enable the lookaside-malloc subsystem */ setupLookaside(db, 0, sqlite4GlobalConfig.szLookaside, sqlite4GlobalConfig.nLookaside); sqlite4_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT); @@ -2494,135 +2360,10 @@ lineno, 20+sqlite4_sourceid()); return SQLITE_CANTOPEN; } -#ifndef SQLITE_OMIT_DEPRECATED -/* -** This is a convenience routine that makes sure that all thread-specific -** data for this thread has been deallocated. -** -** SQLite no longer uses thread-specific data so this routine is now a -** no-op. It is retained for historical compatibility. -*/ -void sqlite4_thread_cleanup(void){ -} -#endif - -/* -** Return meta information about a specific column of a database table. -** See comment in sqlite4.h (sqlite.h.in) for details. -*/ -#ifdef SQLITE_ENABLE_COLUMN_METADATA -int sqlite4_table_column_metadata( - sqlite4 *db, /* Connection handle */ - const char *zDbName, /* Database name or NULL */ - const char *zTableName, /* Table name */ - const char *zColumnName, /* Column name */ - char const **pzDataType, /* OUTPUT: Declared data type */ - char const **pzCollSeq, /* OUTPUT: Collation sequence name */ - int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /* OUTPUT: True if column part of PK */ - int *pAutoinc /* OUTPUT: True if column is auto-increment */ -){ - int rc; - char *zErrMsg = 0; - Table *pTab = 0; - Column *pCol = 0; - int iCol; - - char const *zDataType = 0; - char const *zCollSeq = 0; - int notnull = 0; - int primarykey = 0; - int autoinc = 0; - - /* Ensure the database schema has been loaded */ - sqlite4_mutex_enter(db->mutex); - sqlite4BtreeEnterAll(db); - rc = sqlite4Init(db, &zErrMsg); - if( SQLITE_OK!=rc ){ - goto error_out; - } - - /* Locate the table in question */ - pTab = sqlite4FindTable(db, zTableName, zDbName); - if( !pTab || pTab->pSelect ){ - pTab = 0; - goto error_out; - } - - /* Find the column for which info is requested */ - if( sqlite4IsRowid(zColumnName) ){ - iCol = pTab->iPKey; - if( iCol>=0 ){ - pCol = &pTab->aCol[iCol]; - } - }else{ - for(iCol=0; iColnCol; iCol++){ - pCol = &pTab->aCol[iCol]; - if( 0==sqlite4StrICmp(pCol->zName, zColumnName) ){ - break; - } - } - if( iCol==pTab->nCol ){ - pTab = 0; - goto error_out; - } - } - - /* The following block stores the meta information that will be returned - ** to the caller in local variables zDataType, zCollSeq, notnull, primarykey - ** and autoinc. At this point there are two possibilities: - ** - ** 1. The specified column name was rowid", "oid" or "_rowid_" - ** and there is no explicitly declared IPK column. - ** - ** 2. The table is not a view and the column name identified an - ** explicitly declared column. Copy meta information from *pCol. - */ - if( pCol ){ - zDataType = pCol->zType; - zCollSeq = pCol->zColl; - notnull = pCol->notNull!=0; - primarykey = pCol->isPrimKey!=0; - autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; - }else{ - zDataType = "INTEGER"; - primarykey = 1; - } - if( !zCollSeq ){ - zCollSeq = "BINARY"; - } - -error_out: - sqlite4BtreeLeaveAll(db); - - /* Whether the function call succeeded or failed, set the output parameters - ** to whatever their local counterparts contain. If an error did occur, - ** this has the effect of zeroing all output parameters. - */ - if( pzDataType ) *pzDataType = zDataType; - if( pzCollSeq ) *pzCollSeq = zCollSeq; - if( pNotNull ) *pNotNull = notnull; - if( pPrimaryKey ) *pPrimaryKey = primarykey; - if( pAutoinc ) *pAutoinc = autoinc; - - if( SQLITE_OK==rc && !pTab ){ - sqlite4DbFree(db, zErrMsg); - zErrMsg = sqlite4MPrintf(db, "no such table column: %s.%s", zTableName, - zColumnName); - rc = SQLITE_ERROR; - } - sqlite4Error(db, rc, (zErrMsg?"%s":0), zErrMsg); - sqlite4DbFree(db, zErrMsg); - rc = sqlite4ApiExit(db, rc); - sqlite4_mutex_leave(db->mutex); - return rc; -} -#endif - /* ** Sleep for a little while. Return the amount of time slept. */ int sqlite4_sleep(int ms){ sqlite4_vfs *pVfs; @@ -2645,48 +2386,10 @@ db->errMask = onoff ? 0xffffffff : 0xff; sqlite4_mutex_leave(db->mutex); return SQLITE_OK; } -/* -** Invoke the xFileControl method on a particular database. -*/ -int sqlite4_file_control(sqlite4 *db, const char *zDbName, int op, void *pArg){ - int rc = SQLITE_ERROR; - int iDb; - sqlite4_mutex_enter(db->mutex); - if( zDbName==0 ){ - iDb = 0; - }else{ - for(iDb=0; iDbnDb; iDb++){ - if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break; - } - } - if( iDbnDb ){ - Btree *pBtree = db->aDb[iDb].pBt; - if( pBtree ){ - Pager *pPager; - sqlite4_file *fd; - sqlite4BtreeEnter(pBtree); - pPager = sqlite4BtreePager(pBtree); - assert( pPager!=0 ); - fd = sqlite4PagerFile(pPager); - assert( fd!=0 ); - if( op==SQLITE_FCNTL_FILE_POINTER ){ - *(sqlite4_file**)pArg = fd; - rc = SQLITE_OK; - }else if( fd->pMethods ){ - rc = sqlite4OsFileControl(fd, op, pArg); - }else{ - rc = SQLITE_NOTFOUND; - } - sqlite4BtreeLeave(pBtree); - } - } - sqlite4_mutex_leave(db->mutex); - return rc; -} /* ** Interface to the testing logic. */ int sqlite4_test_control(int op, ...){ @@ -2828,24 +2531,10 @@ int x = va_arg(ap,int); rc = ALWAYS(x); break; } - /* sqlite4_test_control(SQLITE_TESTCTRL_RESERVE, sqlite4 *db, int N) - ** - ** Set the nReserve size to N for the main database on the database - ** connection db. - */ - case SQLITE_TESTCTRL_RESERVE: { - sqlite4 *db = va_arg(ap, sqlite4*); - int x = va_arg(ap,int); - sqlite4_mutex_enter(db->mutex); - sqlite4BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); - sqlite4_mutex_leave(db->mutex); - break; - } - /* sqlite4_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite4 *db, int N) ** ** Enable or disable various optimizations for testing purposes. The ** argument N is a bitmask of optimizations to be disabled. For normal ** operation N should be 0. The idea is that a test program (like the @@ -2971,19 +2660,5 @@ if( z && sqlite4Atoi64(z, &v, sqlite4Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){ bDflt = v; } return bDflt; } - -/* -** Return the filename of the database associated with a database -** connection. -*/ -const char *sqlite4_db_filename(sqlite4 *db, const char *zDbName){ - int i; - for(i=0; inDb; i++){ - if( db->aDb[i].pBt && sqlite4StrICmp(zDbName, db->aDb[i].zName)==0 ){ - return sqlite4BtreeGetFilename(db->aDb[i].pBt); - } - } - return 0; -} DELETED src/memjournal.c Index: src/memjournal.c ================================================================== --- src/memjournal.c +++ /dev/null @@ -1,259 +0,0 @@ -/* -** 2008 October 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to implement an in-memory rollback journal. -** The in-memory rollback journal is used to journal transactions for -** ":memory:" databases and when the journal_mode=MEMORY pragma is used. -*/ -#include "sqliteInt.h" - -/* Forward references to internal structures */ -typedef struct MemJournal MemJournal; -typedef struct FilePoint FilePoint; -typedef struct FileChunk FileChunk; - -/* Space to hold the rollback journal is allocated in increments of -** this many bytes. -** -** The size chosen is a little less than a power of two. That way, -** the FileChunk object will have a size that almost exactly fills -** a power-of-two allocation. This mimimizes wasted space in power-of-two -** memory allocators. -*/ -#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) - -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* -** The rollback journal is composed of a linked list of these structures. -*/ -struct FileChunk { - FileChunk *pNext; /* Next chunk in the journal */ - u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */ -}; - -/* -** An instance of this object serves as a cursor into the rollback journal. -** The cursor can be either for reading or writing. -*/ -struct FilePoint { - sqlite4_int64 iOffset; /* Offset from the beginning of the file */ - FileChunk *pChunk; /* Specific chunk into which cursor points */ -}; - -/* -** This subclass is a subclass of sqlite4_file. Each open memory-journal -** is an instance of this class. -*/ -struct MemJournal { - sqlite4_io_methods *pMethod; /* Parent class. MUST BE FIRST */ - FileChunk *pFirst; /* Head of in-memory chunk-list */ - FilePoint endpoint; /* Pointer to the end of the file */ - FilePoint readpoint; /* Pointer to the end of the last xRead() */ -}; - -/* -** Read data from the in-memory journal file. This is the implementation -** of the sqlite4_vfs.xRead method. -*/ -static int memjrnlRead( - sqlite4_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - MemJournal *p = (MemJournal *)pJfd; - u8 *zOut = zBuf; - int nRead = iAmt; - int iChunkOffset; - FileChunk *pChunk; - - /* SQLite never tries to read past the end of a rollback journal file */ - assert( iOfst+iAmt<=p->endpoint.iOffset ); - - if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ - sqlite4_int64 iOff = 0; - for(pChunk=p->pFirst; - ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; - pChunk=pChunk->pNext - ){ - iOff += JOURNAL_CHUNKSIZE; - } - }else{ - pChunk = p->readpoint.pChunk; - } - - iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE); - do { - int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset; - int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset)); - memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy); - zOut += nCopy; - nRead -= iSpace; - iChunkOffset = 0; - } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); - p->readpoint.iOffset = iOfst+iAmt; - p->readpoint.pChunk = pChunk; - - return SQLITE_OK; -} - -/* -** Write data to the file. -*/ -static int memjrnlWrite( - sqlite4_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - MemJournal *p = (MemJournal *)pJfd; - int nWrite = iAmt; - u8 *zWrite = (u8 *)zBuf; - - /* An in-memory journal file should only ever be appended to. Random - ** access writes are not required by sqlite. - */ - assert( iOfst==p->endpoint.iOffset ); - UNUSED_PARAMETER(iOfst); - - while( nWrite>0 ){ - FileChunk *pChunk = p->endpoint.pChunk; - int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE); - int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset); - - if( iChunkOffset==0 ){ - /* New chunk is required to extend the file. */ - FileChunk *pNew = sqlite4_malloc(sizeof(FileChunk)); - if( !pNew ){ - return SQLITE_IOERR_NOMEM; - } - pNew->pNext = 0; - if( pChunk ){ - assert( p->pFirst ); - pChunk->pNext = pNew; - }else{ - assert( !p->pFirst ); - p->pFirst = pNew; - } - p->endpoint.pChunk = pNew; - } - - memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace); - zWrite += iSpace; - nWrite -= iSpace; - p->endpoint.iOffset += iSpace; - } - - return SQLITE_OK; -} - -/* -** Truncate the file. -*/ -static int memjrnlTruncate(sqlite4_file *pJfd, sqlite_int64 size){ - MemJournal *p = (MemJournal *)pJfd; - FileChunk *pChunk; - assert(size==0); - UNUSED_PARAMETER(size); - pChunk = p->pFirst; - while( pChunk ){ - FileChunk *pTmp = pChunk; - pChunk = pChunk->pNext; - sqlite4_free(pTmp); - } - sqlite4MemJournalOpen(pJfd); - return SQLITE_OK; -} - -/* -** Close the file. -*/ -static int memjrnlClose(sqlite4_file *pJfd){ - memjrnlTruncate(pJfd, 0); - return SQLITE_OK; -} - - -/* -** Sync the file. -** -** Syncing an in-memory journal is a no-op. And, in fact, this routine -** is never called in a working implementation. This implementation -** exists purely as a contingency, in case some malfunction in some other -** part of SQLite causes Sync to be called by mistake. -*/ -static int memjrnlSync(sqlite4_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} - -/* -** Query the size of the file in bytes. -*/ -static int memjrnlFileSize(sqlite4_file *pJfd, sqlite_int64 *pSize){ - MemJournal *p = (MemJournal *)pJfd; - *pSize = (sqlite_int64) p->endpoint.iOffset; - return SQLITE_OK; -} - -/* -** Table of methods for MemJournal sqlite4_file object. -*/ -static const struct sqlite4_io_methods MemJournalMethods = { - 1, /* iVersion */ - memjrnlClose, /* xClose */ - memjrnlRead, /* xRead */ - memjrnlWrite, /* xWrite */ - memjrnlTruncate, /* xTruncate */ - memjrnlSync, /* xSync */ - memjrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnlock */ -}; - -/* -** Open a journal file. -*/ -void sqlite4MemJournalOpen(sqlite4_file *pJfd){ - MemJournal *p = (MemJournal *)pJfd; - assert( EIGHT_BYTE_ALIGNMENT(p) ); - memset(p, 0, sqlite4MemJournalSize()); - p->pMethod = (sqlite4_io_methods*)&MemJournalMethods; -} - -/* -** Return true if the file-handle passed as an argument is -** an in-memory journal -*/ -int sqlite4IsMemJournal(sqlite4_file *pJfd){ - return pJfd->pMethods==&MemJournalMethods; -} - -/* -** Return the number of bytes required to store a MemJournal file descriptor. -*/ -int sqlite4MemJournalSize(void){ - return sizeof(MemJournal); -} DELETED src/notify.c Index: src/notify.c ================================================================== --- src/notify.c +++ /dev/null @@ -1,332 +0,0 @@ -/* -** 2009 March 3 -** -** 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 the implementation of the sqlite4_unlock_notify() -** API method and its associated functionality. -*/ -#include "sqliteInt.h" -#include "btreeInt.h" - -/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - -/* -** Public interfaces: -** -** sqlite4ConnectionBlocked() -** sqlite4ConnectionUnlocked() -** sqlite4ConnectionClosed() -** sqlite4_unlock_notify() -*/ - -#define assertMutexHeld() \ - assert( sqlite4_mutex_held(sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ) - -/* -** Head of a linked list of all sqlite4 objects created by this process -** for which either sqlite4.pBlockingConnection or sqlite4.pUnlockConnection -** is not NULL. This variable may only accessed while the STATIC_MASTER -** mutex is held. -*/ -static sqlite4 *SQLITE_WSD sqlite4BlockedList = 0; - -#ifndef NDEBUG -/* -** This function is a complex assert() that verifies the following -** properties of the blocked connections list: -** -** 1) Each entry in the list has a non-NULL value for either -** pUnlockConnection or pBlockingConnection, or both. -** -** 2) All entries in the list that share a common value for -** xUnlockNotify are grouped together. -** -** 3) If the argument db is not NULL, then none of the entries in the -** blocked connections list have pUnlockConnection or pBlockingConnection -** set to db. This is used when closing connection db. -*/ -static void checkListProperties(sqlite4 *db){ - sqlite4 *p; - for(p=sqlite4BlockedList; p; p=p->pNextBlocked){ - int seen = 0; - sqlite4 *p2; - - /* Verify property (1) */ - assert( p->pUnlockConnection || p->pBlockingConnection ); - - /* Verify property (2) */ - for(p2=sqlite4BlockedList; p2!=p; p2=p2->pNextBlocked){ - if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1; - assert( p2->xUnlockNotify==p->xUnlockNotify || !seen ); - assert( db==0 || p->pUnlockConnection!=db ); - assert( db==0 || p->pBlockingConnection!=db ); - } - } -} -#else -# define checkListProperties(x) -#endif - -/* -** Remove connection db from the blocked connections list. If connection -** db is not currently a part of the list, this function is a no-op. -*/ -static void removeFromBlockedList(sqlite4 *db){ - sqlite4 **pp; - assertMutexHeld(); - for(pp=&sqlite4BlockedList; *pp; pp = &(*pp)->pNextBlocked){ - if( *pp==db ){ - *pp = (*pp)->pNextBlocked; - break; - } - } -} - -/* -** Add connection db to the blocked connections list. It is assumed -** that it is not already a part of the list. -*/ -static void addToBlockedList(sqlite4 *db){ - sqlite4 **pp; - assertMutexHeld(); - for( - pp=&sqlite4BlockedList; - *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify; - pp=&(*pp)->pNextBlocked - ); - db->pNextBlocked = *pp; - *pp = db; -} - -/* -** Obtain the STATIC_MASTER mutex. -*/ -static void enterMutex(void){ - sqlite4_mutex_enter(sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - checkListProperties(0); -} - -/* -** Release the STATIC_MASTER mutex. -*/ -static void leaveMutex(void){ - assertMutexHeld(); - checkListProperties(0); - sqlite4_mutex_leave(sqlite4MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} - -/* -** Register an unlock-notify callback. -** -** This is called after connection "db" has attempted some operation -** but has received an SQLITE_LOCKED error because another connection -** (call it pOther) in the same process was busy using the same shared -** cache. pOther is found by looking at db->pBlockingConnection. -** -** If there is no blocking connection, the callback is invoked immediately, -** before this routine returns. -** -** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate -** a deadlock. -** -** Otherwise, make arrangements to invoke xNotify when pOther drops -** its locks. -** -** Each call to this routine overrides any prior callbacks registered -** on the same "db". If xNotify==0 then any prior callbacks are immediately -** cancelled. -*/ -int sqlite4_unlock_notify( - sqlite4 *db, - void (*xNotify)(void **, int), - void *pArg -){ - int rc = SQLITE_OK; - - sqlite4_mutex_enter(db->mutex); - enterMutex(); - - if( xNotify==0 ){ - removeFromBlockedList(db); - db->pBlockingConnection = 0; - db->pUnlockConnection = 0; - db->xUnlockNotify = 0; - db->pUnlockArg = 0; - }else if( 0==db->pBlockingConnection ){ - /* The blocking transaction has been concluded. Or there never was a - ** blocking transaction. In either case, invoke the notify callback - ** immediately. - */ - xNotify(&pArg, 1); - }else{ - sqlite4 *p; - - for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){} - if( p ){ - rc = SQLITE_LOCKED; /* Deadlock detected. */ - }else{ - db->pUnlockConnection = db->pBlockingConnection; - db->xUnlockNotify = xNotify; - db->pUnlockArg = pArg; - removeFromBlockedList(db); - addToBlockedList(db); - } - } - - leaveMutex(); - assert( !db->mallocFailed ); - sqlite4Error(db, rc, (rc?"database is deadlocked":0)); - sqlite4_mutex_leave(db->mutex); - return rc; -} - -/* -** This function is called while stepping or preparing a statement -** associated with connection db. The operation will return SQLITE_LOCKED -** to the user because it requires a lock that will not be available -** until connection pBlocker concludes its current transaction. -*/ -void sqlite4ConnectionBlocked(sqlite4 *db, sqlite4 *pBlocker){ - enterMutex(); - if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){ - addToBlockedList(db); - } - db->pBlockingConnection = pBlocker; - leaveMutex(); -} - -/* -** This function is called when -** the transaction opened by database db has just finished. Locks held -** by database connection db have been released. -** -** This function loops through each entry in the blocked connections -** list and does the following: -** -** 1) If the sqlite4.pBlockingConnection member of a list entry is -** set to db, then set pBlockingConnection=0. -** -** 2) If the sqlite4.pUnlockConnection member of a list entry is -** set to db, then invoke the configured unlock-notify callback and -** set pUnlockConnection=0. -** -** 3) If the two steps above mean that pBlockingConnection==0 and -** pUnlockConnection==0, remove the entry from the blocked connections -** list. -*/ -void sqlite4ConnectionUnlocked(sqlite4 *db){ - void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */ - int nArg = 0; /* Number of entries in aArg[] */ - sqlite4 **pp; /* Iterator variable */ - void **aArg; /* Arguments to the unlock callback */ - void **aDyn = 0; /* Dynamically allocated space for aArg[] */ - void *aStatic[16]; /* Starter space for aArg[]. No malloc required */ - - aArg = aStatic; - enterMutex(); /* Enter STATIC_MASTER mutex */ - - /* This loop runs once for each entry in the blocked-connections list. */ - for(pp=&sqlite4BlockedList; *pp; /* no-op */ ){ - sqlite4 *p = *pp; - - /* Step 1. */ - if( p->pBlockingConnection==db ){ - p->pBlockingConnection = 0; - } - - /* Step 2. */ - if( p->pUnlockConnection==db ){ - assert( p->xUnlockNotify ); - if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){ - xUnlockNotify(aArg, nArg); - nArg = 0; - } - - sqlite4BeginBenignMalloc(); - assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) ); - assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn ); - if( (!aDyn && nArg==(int)ArraySize(aStatic)) - || (aDyn && nArg==(int)(sqlite4MallocSize(aDyn)/sizeof(void*))) - ){ - /* The aArg[] array needs to grow. */ - void **pNew = (void **)sqlite4Malloc(nArg*sizeof(void *)*2); - if( pNew ){ - memcpy(pNew, aArg, nArg*sizeof(void *)); - sqlite4_free(aDyn); - aDyn = aArg = pNew; - }else{ - /* This occurs when the array of context pointers that need to - ** be passed to the unlock-notify callback is larger than the - ** aStatic[] array allocated on the stack and the attempt to - ** allocate a larger array from the heap has failed. - ** - ** This is a difficult situation to handle. Returning an error - ** code to the caller is insufficient, as even if an error code - ** is returned the transaction on connection db will still be - ** closed and the unlock-notify callbacks on blocked connections - ** will go unissued. This might cause the application to wait - ** indefinitely for an unlock-notify callback that will never - ** arrive. - ** - ** Instead, invoke the unlock-notify callback with the context - ** array already accumulated. We can then clear the array and - ** begin accumulating any further context pointers without - ** requiring any dynamic allocation. This is sub-optimal because - ** it means that instead of one callback with a large array of - ** context pointers the application will receive two or more - ** callbacks with smaller arrays of context pointers, which will - ** reduce the applications ability to prioritize multiple - ** connections. But it is the best that can be done under the - ** circumstances. - */ - xUnlockNotify(aArg, nArg); - nArg = 0; - } - } - sqlite4EndBenignMalloc(); - - aArg[nArg++] = p->pUnlockArg; - xUnlockNotify = p->xUnlockNotify; - p->pUnlockConnection = 0; - p->xUnlockNotify = 0; - p->pUnlockArg = 0; - } - - /* Step 3. */ - if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){ - /* Remove connection p from the blocked connections list. */ - *pp = p->pNextBlocked; - p->pNextBlocked = 0; - }else{ - pp = &p->pNextBlocked; - } - } - - if( nArg!=0 ){ - xUnlockNotify(aArg, nArg); - } - sqlite4_free(aDyn); - leaveMutex(); /* Leave STATIC_MASTER mutex */ -} - -/* -** This is called when the database connection passed as an argument is -** being closed. The connection is removed from the blocked list. -*/ -void sqlite4ConnectionClosed(sqlite4 *db){ - sqlite4ConnectionUnlocked(db); - enterMutex(); - removeFromBlockedList(db); - checkListProperties(db); - leaveMutex(); -} -#endif Index: src/os.c ================================================================== --- src/os.c +++ src/os.c @@ -15,46 +15,10 @@ */ #define _SQLITE_OS_C_ 1 #include "sqliteInt.h" #undef _SQLITE_OS_C_ -/* -** The default SQLite sqlite4_vfs implementations do not allocate -** memory (actually, os_unix.c allocates a small amount of memory -** from within OsOpen()), but some third-party implementations may. -** So we test the effects of a malloc() failing and the sqlite4OsXXX() -** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro. -** -** The following functions are instrumented for malloc() failure -** testing: -** -** sqlite4OsRead() -** sqlite4OsWrite() -** sqlite4OsSync() -** sqlite4OsFileSize() -** sqlite4OsLock() -** sqlite4OsCheckReservedLock() -** sqlite4OsFileControl() -** sqlite4OsShmMap() -** sqlite4OsOpen() -** sqlite4OsDelete() -** sqlite4OsAccess() -** sqlite4OsFullPathname() -** -*/ -#if defined(SQLITE_TEST) -int sqlite4_memdebug_vfs_oom_test = 1; - #define DO_OS_MALLOC_TEST(x) \ - if (sqlite4_memdebug_vfs_oom_test && (!x || !sqlite4IsMemJournal(x))) { \ - void *pTstAlloc = sqlite4Malloc(10); \ - if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ - sqlite4_free(pTstAlloc); \ - } -#else - #define DO_OS_MALLOC_TEST(x) -#endif - /* ** The following routines are convenience wrappers around methods ** of the sqlite4_file object. This is mostly just syntactic sugar. All ** of this would be completely automatic if SQLite were coded using ** C++ instead of plain old C. @@ -66,37 +30,31 @@ pId->pMethods = 0; } return rc; } int sqlite4OsRead(sqlite4_file *id, void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xRead(id, pBuf, amt, offset); } int sqlite4OsWrite(sqlite4_file *id, const void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xWrite(id, pBuf, amt, offset); } int sqlite4OsTruncate(sqlite4_file *id, i64 size){ return id->pMethods->xTruncate(id, size); } int sqlite4OsSync(sqlite4_file *id, int flags){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xSync(id, flags); } int sqlite4OsFileSize(sqlite4_file *id, i64 *pSize){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xFileSize(id, pSize); } int sqlite4OsLock(sqlite4_file *id, int lockType){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xLock(id, lockType); } int sqlite4OsUnlock(sqlite4_file *id, int lockType){ return id->pMethods->xUnlock(id, lockType); } int sqlite4OsCheckReservedLock(sqlite4_file *id, int *pResOut){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xCheckReservedLock(id, pResOut); } /* ** Use sqlite4OsFileControl() when we are doing something that might fail @@ -105,11 +63,10 @@ ** really care if the VFS receives and understands the information since it ** is only a hint and can be safely ignored. The sqlite4OsFileControlHint() ** routine has no return value since the return value would be meaningless. */ int sqlite4OsFileControl(sqlite4_file *id, int op, void *pArg){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xFileControl(id, op, pArg); } void sqlite4OsFileControlHint(sqlite4_file *id, int op, void *pArg){ (void)id->pMethods->xFileControl(id, op, pArg); } @@ -135,11 +92,10 @@ int iPage, int pgsz, int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Pointer to mapping */ ){ - DO_OS_MALLOC_TEST(id); return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); } /* ** The next group of routines are convenience wrappers around the @@ -151,40 +107,36 @@ sqlite4_file *pFile, int flags, int *pFlagsOut ){ int rc; - DO_OS_MALLOC_TEST(0); /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before ** reaching the VFS. */ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } int sqlite4OsDelete(sqlite4_vfs *pVfs, const char *zPath, int dirSync){ - DO_OS_MALLOC_TEST(0); assert( dirSync==0 || dirSync==1 ); return pVfs->xDelete(pVfs, zPath, dirSync); } int sqlite4OsAccess( sqlite4_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ - DO_OS_MALLOC_TEST(0); return pVfs->xAccess(pVfs, zPath, flags, pResOut); } int sqlite4OsFullPathname( sqlite4_vfs *pVfs, const char *zPath, int nPathOut, char *zPathOut ){ - DO_OS_MALLOC_TEST(0); zPathOut[0] = 0; return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); } #ifndef SQLITE_OMIT_LOAD_EXTENSION void *sqlite4OsDlOpen(sqlite4_vfs *pVfs, const char *zPath){ DELETED src/pager.c Index: src/pager.c ================================================================== --- src/pager.c +++ /dev/null @@ -1,6385 +0,0 @@ -/* -** 2001 September 15 -** -** 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 is the implementation of the page cache subsystem or "pager". -** -** The pager is used to access a database disk file. It implements -** atomic commit and rollback through the use of a journal file that -** is separate from the database file. The pager also implements file -** locking to prevent two processes from writing the same database -** file simultaneously, or one process from reading the database while -** another is writing. -*/ -#ifndef SQLITE_OMIT_DISKIO -#include "sqliteInt.h" - - -/******************* NOTES ON THE DESIGN OF THE PAGER ************************ -** -** This comment block describes invariants that hold when using a rollback -** journal. These invariants do not apply for journal_mode=WAL, -** journal_mode=MEMORY, or journal_mode=OFF. -** -** Within this comment block, a page is deemed to have been synced -** automatically as soon as it is written when PRAGMA synchronous=OFF. -** Otherwise, the page is not synced until the xSync method of the VFS -** is called successfully on the file containing the page. -** -** Definition: A page of the database file is said to be "overwriteable" if -** one or more of the following are true about the page: -** -** (a) The original content of the page as it was at the beginning of -** the transaction has been written into the rollback journal and -** synced. -** -** (b) The page was a freelist leaf page at the start of the transaction. -** -** (c) The page number is greater than the largest page that existed in -** the database file at the start of the transaction. -** -** (1) A page of the database file is never overwritten unless one of the -** following are true: -** -** (a) The page and all other pages on the same sector are overwriteable. -** -** (b) The atomic page write optimization is enabled, and the entire -** transaction other than the update of the transaction sequence -** number consists of a single page change. -** -** (2) The content of a page written into the rollback journal exactly matches -** both the content in the database when the rollback journal was written -** and the content in the database at the beginning of the current -** transaction. -** -** (3) Writes to the database file are an integer multiple of the page size -** in length and are aligned on a page boundary. -** -** (4) Reads from the database file are either aligned on a page boundary and -** an integer multiple of the page size in length or are taken from the -** first 100 bytes of the database file. -** -** (5) All writes to the database file are synced prior to the rollback journal -** being deleted, truncated, or zeroed. -** -** (6) If a master journal file is used, then all writes to the database file -** are synced prior to the master journal being deleted. -** -** Definition: Two databases (or the same database at two points it time) -** are said to be "logically equivalent" if they give the same answer to -** all queries. Note in particular the the content of freelist leaf -** pages can be changed arbitarily without effecting the logical equivalence -** of the database. -** -** (7) At any time, if any subset, including the empty set and the total set, -** of the unsynced changes to a rollback journal are removed and the -** journal is rolled back, the resulting database file will be logical -** equivalent to the database file at the beginning of the transaction. -** -** (8) When a transaction is rolled back, the xTruncate method of the VFS -** is called to restore the database file to the same size it was at -** the beginning of the transaction. (In some VFSes, the xTruncate -** method is a no-op, but that does not change the fact the SQLite will -** invoke it.) -** -** (9) Whenever the database file is modified, at least one bit in the range -** of bytes from 24 through 39 inclusive will be changed prior to releasing -** the EXCLUSIVE lock, thus signaling other connections on the same -** database to flush their caches. -** -** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less -** than one billion transactions. -** -** (11) A database file is well-formed at the beginning and at the conclusion -** of every transaction. -** -** (12) An EXCLUSIVE lock is held on the database file when writing to -** the database file. -** -** (13) A SHARED lock is held on the database file while reading any -** content out of the database file. -** -******************************************************************************/ - -/* -** Macros for troubleshooting. Normally turned off -*/ -#if 0 -int sqlite4PagerTrace=1; /* True to enable tracing */ -#define sqlite4DebugPrintf printf -#define PAGERTRACE(X) if( sqlite4PagerTrace ){ sqlite4DebugPrintf X; } -#else -#define PAGERTRACE(X) -#endif - -/* -** The following two macros are used within the PAGERTRACE() macros above -** to print out file-descriptors. -** -** PAGERID() takes a pointer to a Pager struct as its argument. The -** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite4_file -** struct as its argument. -*/ -#define PAGERID(p) ((int)(p->fd)) -#define FILEHANDLEID(fd) ((int)fd) - -/* -** The Pager.eState variable stores the current 'state' of a pager. A -** pager may be in any one of the seven states shown in the following -** state diagram. -** -** OPEN <------+------+ -** | | | -** V | | -** +---------> READER-------+ | -** | | | -** | V | -** |<-------WRITER_LOCKED------> ERROR -** | | ^ -** | V | -** |<------WRITER_CACHEMOD-------->| -** | | | -** | V | -** |<-------WRITER_DBMOD---------->| -** | | | -** | V | -** +<------WRITER_FINISHED-------->+ -** -** -** List of state transitions and the C [function] that performs each: -** -** OPEN -> READER [sqlite4PagerSharedLock] -** READER -> OPEN [pager_unlock] -** -** READER -> WRITER_LOCKED [sqlite4PagerBegin] -** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal] -** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal] -** WRITER_DBMOD -> WRITER_FINISHED [sqlite4PagerCommitPhaseOne] -** WRITER_*** -> READER [pager_end_transaction] -** -** WRITER_*** -> ERROR [pager_error] -** ERROR -> OPEN [pager_unlock] -** -** -** OPEN: -** -** The pager starts up in this state. Nothing is guaranteed in this -** state - the file may or may not be locked and the database size is -** unknown. The database may not be read or written. -** -** * No read or write transaction is active. -** * Any lock, or no lock at all, may be held on the database file. -** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted. -** -** READER: -** -** In this state all the requirements for reading the database in -** rollback (non-WAL) mode are met. Unless the pager is (or recently -** was) in exclusive-locking mode, a user-level read transaction is -** open. The database size is known in this state. -** -** A connection running with locking_mode=normal enters this state when -** it opens a read-transaction on the database and returns to state -** OPEN after the read-transaction is completed. However a connection -** running in locking_mode=exclusive (including temp databases) remains in -** this state even after the read-transaction is closed. The only way -** a locking_mode=exclusive connection can transition from READER to OPEN -** is via the ERROR state (see below). -** -** * A read transaction may be active (but a write-transaction cannot). -** * A SHARED or greater lock is held on the database file. -** * The dbSize variable may be trusted (even if a user-level read -** transaction is not active). The dbOrigSize and dbFileSize variables -** may not be trusted at this point. -** * If the database is a WAL database, then the WAL connection is open. -** * Even if a read-transaction is not open, it is guaranteed that -** there is no hot-journal in the file-system. -** -** WRITER_LOCKED: -** -** The pager moves to this state from READER when a write-transaction -** is first opened on the database. In WRITER_LOCKED state, all locks -** required to start a write-transaction are held, but no actual -** modifications to the cache or database have taken place. -** -** In rollback mode, a RESERVED or (if the transaction was opened with -** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when -** moving to this state, but the journal file is not written to or opened -** to in this state. If the transaction is committed or rolled back while -** in WRITER_LOCKED state, all that is required is to unlock the database -** file. -** -** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file. -** If the connection is running with locking_mode=exclusive, an attempt -** is made to obtain an EXCLUSIVE lock on the database file. -** -** * A write transaction is active. -** * If the connection is open in rollback-mode, a RESERVED or greater -** lock is held on the database file. -** * If the connection is open in WAL-mode, a WAL write transaction -** is open (i.e. sqlite4WalBeginWriteTransaction() has been successfully -** called). -** * The dbSize, dbOrigSize and dbFileSize variables are all valid. -** * The contents of the pager cache have not been modified. -** * The journal file may or may not be open. -** * Nothing (not even the first header) has been written to the journal. -** -** WRITER_CACHEMOD: -** -** A pager moves from WRITER_LOCKED state to this state when a page is -** first modified by the upper layer. In rollback mode the journal file -** is opened (if it is not already open) and a header written to the -** start of it. The database file on disk has not been modified. -** -** * A write transaction is active. -** * A RESERVED or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** to it, but the header has not been synced to disk. -** * The contents of the page cache have been modified. -** -** WRITER_DBMOD: -** -** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state -** when it modifies the contents of the database file. WAL connections -** never enter this state (since they do not modify the database file, -** just the log file). -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** and synced to disk. -** * The contents of the page cache have been modified (and possibly -** written to disk). -** -** WRITER_FINISHED: -** -** It is not possible for a WAL connection to enter this state. -** -** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD -** state after the entire transaction has been successfully written into the -** database file. In this state the transaction may be committed simply -** by finalizing the journal file. Once in WRITER_FINISHED state, it is -** not possible to modify the database further. At this point, the upper -** layer must either commit or rollback the transaction. -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * All writing and syncing of journal and database data has finished. -** If no error occured, all that remains is to finalize the journal to -** commit the transaction. If an error did occur, the caller will need -** to rollback the transaction. -** -** ERROR: -** -** The ERROR state is entered when an IO or disk-full error (including -** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it -** difficult to be sure that the in-memory pager state (cache contents, -** db size etc.) are consistent with the contents of the file-system. -** -** Temporary pager files may enter the ERROR state, but in-memory pagers -** cannot. -** -** For example, if an IO error occurs while performing a rollback, -** the contents of the page-cache may be left in an inconsistent state. -** At this point it would be dangerous to change back to READER state -** (as usually happens after a rollback). Any subsequent readers might -** report database corruption (due to the inconsistent cache), and if -** they upgrade to writers, they may inadvertently corrupt the database -** file. To avoid this hazard, the pager switches into the ERROR state -** instead of READER following such an error. -** -** Once it has entered the ERROR state, any attempt to use the pager -** to read or write data returns an error. Eventually, once all -** outstanding transactions have been abandoned, the pager is able to -** transition back to OPEN state, discarding the contents of the -** page-cache and any other in-memory state at the same time. Everything -** is reloaded from disk (and, if necessary, hot-journal rollback peformed) -** when a read-transaction is next opened on the pager (transitioning -** the pager into READER state). At that point the system has recovered -** from the error. -** -** Specifically, the pager jumps into the ERROR state if: -** -** 1. An error occurs while attempting a rollback. This happens in -** function sqlite4PagerRollback(). -** -** 2. An error occurs while attempting to finalize a journal file -** following a commit in function sqlite4PagerCommitPhaseTwo(). -** -** 3. An error occurs while attempting to write to the journal or -** database file in function pagerStress() in order to free up -** memory. -** -** In other cases, the error is returned to the b-tree layer. The b-tree -** layer then attempts a rollback operation. If the error condition -** persists, the pager enters the ERROR state via condition (1) above. -** -** Condition (3) is necessary because it can be triggered by a read-only -** statement executed within a transaction. In this case, if the error -** code were simply returned to the user, the b-tree layer would not -** automatically attempt a rollback, as it assumes that an error in a -** read-only statement cannot leave the pager in an internally inconsistent -** state. -** -** * The Pager.errCode variable is set to something other than SQLITE_OK. -** * There are one or more outstanding references to pages (after the -** last reference is dropped the pager should move back to OPEN state). -** * The pager is not an in-memory pager. -** -** -** Notes: -** -** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the -** connection is open in WAL mode. A WAL connection is always in one -** of the first four states. -** -** * Normally, a connection open in exclusive mode is never in PAGER_OPEN -** state. There are two exceptions: immediately after exclusive-mode has -** been turned on (and before any read or write transactions are -** executed), and when the pager is leaving the "error state". -** -** * See also: assert_pager_state(). -*/ -#define PAGER_OPEN 0 -#define PAGER_READER 1 -#define PAGER_WRITER_LOCKED 2 -#define PAGER_WRITER_CACHEMOD 3 -#define PAGER_WRITER_DBMOD 4 -#define PAGER_WRITER_FINISHED 5 -#define PAGER_ERROR 6 - -/* -** The Pager.eLock variable is almost always set to one of the -** following locking-states, according to the lock currently held on -** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** This variable is kept up to date as locks are taken and released by -** the pagerLockDb() and pagerUnlockDb() wrappers. -** -** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY -** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not -** the operation was successful. In these circumstances pagerLockDb() and -** pagerUnlockDb() take a conservative approach - eLock is always updated -** when unlocking the file, and only updated when locking the file if the -** VFS call is successful. This way, the Pager.eLock variable may be set -** to a less exclusive (lower) value than the lock that is actually held -** at the system level, but it is never set to a more exclusive value. -** -** This is usually safe. If an xUnlock fails or appears to fail, there may -** be a few redundant xLock() calls or a lock may be held for longer than -** required, but nothing really goes wrong. -** -** The exception is when the database file is unlocked as the pager moves -** from ERROR to OPEN state. At this point there may be a hot-journal file -** in the file-system that needs to be rolled back (as part of a OPEN->SHARED -** transition, by the same pager or any other). If the call to xUnlock() -** fails at this point and the pager is left holding an EXCLUSIVE lock, this -** can confuse the call to xCheckReservedLock() call made later as part -** of hot-journal detection. -** -** xCheckReservedLock() is defined as returning true "if there is a RESERVED -** lock held by this process or any others". So xCheckReservedLock may -** return true because the caller itself is holding an EXCLUSIVE lock (but -** doesn't know it because of a previous error in xUnlock). If this happens -** a hot-journal may be mistaken for a journal being created by an active -** transaction in another process, causing SQLite to read from the database -** without rolling it back. -** -** To work around this, if a call to xUnlock() fails when unlocking the -** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It -** is only changed back to a real locking state after a successful call -** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition -** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK -** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE -** lock on the database file before attempting to roll it back. See function -** PagerSharedLock() for more detail. -** -** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in -** PAGER_OPEN state. -*/ -#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) - -/* -** A macro used for invoking the codec if there is one -*/ -#ifdef SQLITE_HAS_CODEC -# define CODEC1(P,D,N,X,E) \ - if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } -# define CODEC2(P,D,N,X,E,O) \ - if( P->xCodec==0 ){ O=(char*)D; }else \ - if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; } -#else -# define CODEC1(P,D,N,X,E) /* NO-OP */ -# define CODEC2(P,D,N,X,E,O) O=(char*)D -#endif - -/* -** The maximum allowed sector size. 64KiB. If the xSectorsize() method -** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. -** This could conceivably cause corruption following a power failure on -** such a system. This is currently an undocumented limit. -*/ -#define MAX_SECTOR_SIZE 0x10000 - -/* -** An instance of the following structure is allocated for each active -** savepoint and statement transaction in the system. All such structures -** are stored in the Pager.aSavepoint[] array, which is allocated and -** resized using sqlite4Realloc(). -** -** When a savepoint is created, the PagerSavepoint.iHdrOffset field is -** set to 0. If a journal-header is written into the main journal while -** the savepoint is active, then iHdrOffset is set to the byte offset -** immediately following the last journal record written into the main -** journal before the journal-header. This is required during savepoint -** rollback (see pagerPlaybackSavepoint()). -*/ -typedef struct PagerSavepoint PagerSavepoint; -struct PagerSavepoint { - i64 iOffset; /* Starting offset in main journal */ - i64 iHdrOffset; /* See above */ - Bitvec *pInSavepoint; /* Set of pages in this savepoint */ - Pgno nOrig; /* Original number of pages in file */ - Pgno iSubRec; /* Index of first record in sub-journal */ -}; - -/* -** A open page cache is an instance of struct Pager. A description of -** some of the more important member variables follows: -** -** eState -** -** The current 'state' of the pager object. See the comment and state -** diagram above for a description of the pager state. -** -** eLock -** -** For a real on-disk database, the current lock held on the database file - -** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** -** For a temporary or in-memory database (neither of which require any -** locks), this variable is always set to EXCLUSIVE_LOCK. Since such -** databases always have Pager.exclusiveMode==1, this tricks the pager -** logic into thinking that it already has all the locks it will ever -** need (and no reason to release them). -** -** In some (obscure) circumstances, this variable may also be set to -** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for -** details. -** -** changeCountDone -** -** This boolean variable is used to make sure that the change-counter -** (the 4-byte header field at byte offset 24 of the database file) is -** not updated more often than necessary. -** -** It is set to true when the change-counter field is updated, which -** can only happen if an exclusive lock is held on the database file. -** It is cleared (set to false) whenever an exclusive lock is -** relinquished on the database file. Each time a transaction is committed, -** The changeCountDone flag is inspected. If it is true, the work of -** updating the change-counter is omitted for the current transaction. -** -** This mechanism means that when running in exclusive mode, a connection -** need only update the change-counter once, for the first transaction -** committed. -** -** setMaster -** -** When PagerCommitPhaseOne() is called to commit a transaction, it may -** (or may not) specify a master-journal name to be written into the -** journal file before it is synced to disk. -** -** Whether or not a journal file contains a master-journal pointer affects -** the way in which the journal file is finalized after the transaction is -** committed or rolled back when running in "journal_mode=PERSIST" mode. -** If a journal file does not contain a master-journal pointer, it is -** finalized by overwriting the first journal header with zeroes. If -** it does contain a master-journal pointer the journal file is finalized -** by truncating it to zero bytes, just as if the connection were -** running in "journal_mode=truncate" mode. -** -** Journal files that contain master journal pointers cannot be finalized -** simply by overwriting the first journal-header with zeroes, as the -** master journal pointer could interfere with hot-journal rollback of any -** subsequently interrupted transaction that reuses the journal file. -** -** The flag is cleared as soon as the journal file is finalized (either -** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the -** journal file from being successfully finalized, the setMaster flag -** is cleared anyway (and the pager will move to ERROR state). -** -** doNotSpill, doNotSyncSpill -** -** These two boolean variables control the behaviour of cache-spills -** (calls made by the pcache module to the pagerStress() routine to -** write cached data to the file-system in order to free up memory). -** -** When doNotSpill is non-zero, writing to the database from pagerStress() -** is disabled altogether. This is done in a very obscure case that -** comes up during savepoint rollback that requires the pcache module -** to allocate a new page to prevent the journal file from being written -** while it is being traversed by code in pager_playback(). -** -** If doNotSyncSpill is non-zero, writing to the database from pagerStress() -** is permitted, but syncing the journal file is not. This flag is set -** by sqlite4PagerWrite() when the file-system sector-size is larger than -** the database page-size in order to prevent a journal sync from happening -** in between the journalling of two pages on the same sector. -** -** subjInMemory -** -** This is a boolean variable. If true, then any required sub-journal -** is opened as an in-memory journal file. If false, then in-memory -** sub-journals are only used for in-memory pager files. -** -** This variable is updated by the upper layer each time a new -** write-transaction is opened. -** -** dbSize, dbOrigSize, dbFileSize -** -** Variable dbSize is set to the number of pages in the database file. -** It is valid in PAGER_READER and higher states (all states except for -** OPEN and ERROR). -** -** dbSize is set based on the size of the database file, which may be -** larger than the size of the database (the value stored at offset -** 28 of the database header by the btree). If the size of the file -** is not an integer multiple of the page-size, the value stored in -** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2). -** Except, any file that is greater than 0 bytes in size is considered -** to have at least one page. (i.e. a 1KB file with 2K page-size leads -** to dbSize==1). -** -** During a write-transaction, if pages with page-numbers greater than -** dbSize are modified in the cache, dbSize is updated accordingly. -** Similarly, if the database is truncated using PagerTruncateImage(), -** dbSize is updated. -** -** Variables dbOrigSize and dbFileSize are valid in states -** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize -** variable at the start of the transaction. It is used during rollback, -** and to determine whether or not pages need to be journalled before -** being modified. -** -** Throughout a write-transaction, dbFileSize contains the size of -** the file on disk in pages. It is set to a copy of dbSize when the -** write-transaction is first opened, and updated when VFS calls are made -** to write or truncate the database file on disk. -** -** The only reason the dbFileSize variable is required is to suppress -** unnecessary calls to xTruncate() after committing a transaction. If, -** when a transaction is committed, the dbFileSize variable indicates -** that the database file is larger than the database image (Pager.dbSize), -** pager_truncate() is called. The pager_truncate() call uses xFilesize() -** to measure the database file on disk, and then truncates it if required. -** dbFileSize is not used when rolling back a transaction. In this case -** pager_truncate() is called unconditionally (which means there may be -** a call to xFilesize() that is not strictly required). In either case, -** pager_truncate() may cause the file to become smaller or larger. -** -** dbHintSize -** -** The dbHintSize variable is used to limit the number of calls made to -** the VFS xFileControl(FCNTL_SIZE_HINT) method. -** -** dbHintSize is set to a copy of the dbSize variable when a -** write-transaction is opened (at the same time as dbFileSize and -** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called, -** dbHintSize is increased to the number of pages that correspond to the -** size-hint passed to the method call. See pager_write_pagelist() for -** details. -** -** errCode -** -** The Pager.errCode variable is only ever used in PAGER_ERROR state. It -** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode -** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX -** sub-codes. -*/ -struct Pager { - sqlite4_vfs *pVfs; /* OS functions to use for IO */ - u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ - u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ - u8 useJournal; /* Use a rollback journal on this file */ - u8 noSync; /* Do not sync the journal if true */ - u8 fullSync; /* Do extra syncs of the journal for robustness */ - 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 */ - - /************************************************************************** - ** The following block contains those class members that change during - ** routine opertion. Class members not in this block are either fixed - ** when the pager is first created or else only change when there is a - ** significant mode change (such as changing the page_size, locking_mode, - ** or the journal_mode). From another view, these class members describe - ** the "state" of the pager, while other class members describe the - ** "configuration" of the pager. - */ - u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ - u8 eLock; /* Current lock held on database file */ - u8 changeCountDone; /* Set after incrementing the change-counter */ - u8 setMaster; /* True if a m-j name has been written to jrnl */ - u8 doNotSpill; /* Do not spill the cache when non-zero */ - u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */ - u8 subjInMemory; /* True to use in-memory sub-journals */ - Pgno dbSize; /* Number of pages in the database */ - Pgno dbOrigSize; /* dbSize before the current transaction */ - Pgno dbFileSize; /* Number of pages in the database file */ - Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ - int errCode; /* One of several kinds of errors */ - int nRec; /* Pages journalled since last j-header written */ - u32 cksumInit; /* Quasi-random value added to every checksum */ - u32 nSubRec; /* Number of records written to sub-journal */ - Bitvec *pInJournal; /* One bit for each page in the database file */ - sqlite4_file *fd; /* File descriptor for database */ - sqlite4_file *jfd; /* File descriptor for main journal */ - sqlite4_file *sjfd; /* File descriptor for sub-journal */ - i64 journalOff; /* Current write offset in the journal file */ - i64 journalHdr; /* Byte offset to previous journal header */ - sqlite4_backup *pBackup; /* Pointer to list of ongoing backup processes */ - PagerSavepoint *aSavepoint; /* Array of active savepoints */ - int nSavepoint; /* Number of elements in aSavepoint[] */ - char dbFileVers[16]; /* Changes whenever database file changes */ - /* - ** End of the routinely-changing class members - ***************************************************************************/ - - u16 nExtra; /* Add this many bytes to each in-memory page */ - i16 nReserve; /* Number of unused bytes at end of each page */ - u32 vfsFlags; /* Flags for sqlite4_vfs.xOpen() */ - u32 sectorSize; /* Assumed sector size during rollback */ - int pageSize; /* Number of bytes in a page */ - Pgno mxPgno; /* Maximum allowed size of the database */ - i64 journalSizeLimit; /* Size limit for persistent journal files */ - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - int (*xBusyHandler)(void*); /* Function to call when busy */ - void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int nHit, nMiss; /* Total cache hits and misses */ -#ifdef SQLITE_TEST - int nRead, nWrite; /* Database pages read/written */ -#endif - void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ -#ifdef SQLITE_HAS_CODEC - void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ - void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ - void (*xCodecFree)(void*); /* Destructor for the codec */ - void *pCodec; /* First argument to xCodec... methods */ -#endif - char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ - PCache *pPCache; /* Pointer to page cache object */ -}; - -/* -** The following global variables hold counters used for -** testing purposes only. These variables do not exist in -** a non-testing build. These variables are not thread-safe. -*/ -#ifdef SQLITE_TEST -int sqlite4_pager_readdb_count = 0; /* Number of full pages read from DB */ -int sqlite4_pager_writedb_count = 0; /* Number of full pages written to DB */ -int sqlite4_pager_writej_count = 0; /* Number of pages written to journal */ -# define PAGER_INCR(v) v++ -#else -# define PAGER_INCR(v) -#endif - - - -/* -** Journal files begin with the following magic string. The data -** was obtained from /dev/random. It is used only as a sanity check. -** -** Since version 2.8.0, the journal format contains additional sanity -** checking information. If the power fails while the journal is being -** written, semi-random garbage data might appear in the journal -** file after power is restored. If an attempt is then made -** to roll the journal back, the database could be corrupted. The additional -** sanity checking data is an attempt to discover the garbage in the -** journal and ignore it. -** -** The sanity checking information for the new journal format consists -** of a 32-bit checksum on each page of data. The checksum covers both -** the page number and the pPager->pageSize bytes of data for the page. -** This cksum is initialized to a 32-bit random value that appears in the -** journal file right after the header. The random initializer is important, -** because garbage data that appears at the end of a journal is likely -** data that was once in other files that have now been deleted. If the -** garbage data came from an obsolete journal file, the checksums might -** be correct. But by initializing the checksum to random value which -** is different for every journal, we minimize that risk. -*/ -static const unsigned char aJournalMagic[] = { - 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7, -}; - -/* -** The size of the of each page record in the journal is given by -** the following macro. -*/ -#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) - -/* -** The journal header size for this pager. This is usually the same -** size as a single disk sector. See also setSectorSize(). -*/ -#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) - -/* -** The macro MEMDB is true if we are dealing with an in-memory database. -** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set, -** the value of MEMDB will be a constant and the compiler will optimize -** out code that would never execute. -*/ -#ifdef SQLITE_OMIT_MEMORYDB -# define MEMDB 0 -#else -# define MEMDB pPager->memDb -#endif - -/* -** The maximum legal page number is (2^31 - 1). -*/ -#define PAGER_MAX_PGNO 2147483647 - -/* -** The argument to this macro is a file descriptor (type sqlite4_file*). -** Return 0 if it is not open, or non-zero (but not 1) if it is. -** -** This is so that expressions can be written as: -** -** if( isOpen(pPager->jfd) ){ ... -** -** instead of -** -** if( pPager->jfd->pMethods ){ ... -*/ -#define isOpen(pFd) ((pFd)->pMethods) - -/* -** Return true if this pager uses a write-ahead log instead of the usual -** rollback journal. Otherwise false. -*/ -#define pagerUseWal(x) 0 -#define pagerRollbackWal(x) 0 -#define pagerWalFrames(v,w,x,y) 0 -#define pagerOpenWalIfPresent(z) SQLITE_OK -#define pagerBeginReadTransaction(z) SQLITE_OK - -#ifndef NDEBUG -/* -** Usage: -** -** assert( assert_pager_state(pPager) ); -** -** This function runs many asserts to try to find inconsistencies in -** the internal state of the Pager object. -*/ -static int assert_pager_state(Pager *p){ - Pager *pPager = p; - - /* State must be valid. */ - assert( p->eState==PAGER_OPEN - || p->eState==PAGER_READER - || p->eState==PAGER_WRITER_LOCKED - || p->eState==PAGER_WRITER_CACHEMOD - || p->eState==PAGER_WRITER_DBMOD - || p->eState==PAGER_WRITER_FINISHED - || p->eState==PAGER_ERROR - ); - - /* Regardless of the current state, a temp-file connection always behaves - ** as if it has an exclusive lock on the database file. It never updates - ** the change-counter field, so the changeCountDone flag is always set. - */ - assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK ); - assert( p->tempFile==0 || pPager->changeCountDone ); - - /* If the useJournal flag is clear, the journal-mode must be "OFF". - ** And if the journal-mode is "OFF", the journal file must not be open. - */ - assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); - assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); - - /* Check that MEMDB implies noSync. And an in-memory journal. Since - ** this means an in-memory pager performs no IO at all, it cannot encounter - ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing - ** a journal file. (although the in-memory journal implementation may - ** return SQLITE_IOERR_NOMEM while the journal file is being written). It - ** is therefore not possible for an in-memory pager to enter the ERROR - ** state. - */ - if( MEMDB ){ - assert( p->noSync ); - assert( p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_MEMORY - ); - assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); - assert( pagerUseWal(p)==0 ); - } - - /* If changeCountDone is set, a RESERVED lock or greater must be held - ** on the file. - */ - assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); - assert( p->eLock!=PENDING_LOCK ); - - switch( p->eState ){ - case PAGER_OPEN: - assert( !MEMDB ); - assert( pPager->errCode==SQLITE_OK ); - assert( sqlite4PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile ); - break; - - case PAGER_READER: - assert( pPager->errCode==SQLITE_OK ); - assert( p->eLock!=UNKNOWN_LOCK ); - assert( p->eLock>=SHARED_LOCK ); - break; - - case PAGER_WRITER_LOCKED: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - assert( p->eLock>=RESERVED_LOCK ); - } - assert( pPager->dbSize==pPager->dbOrigSize ); - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - assert( pPager->setMaster==0 ); - break; - - case PAGER_WRITER_CACHEMOD: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - /* It is possible that if journal_mode=wal here that neither the - ** journal file nor the WAL file are open. This happens during - ** a rollback transaction that switches from journal_mode=off - ** to journal_mode=wal. - */ - assert( p->eLock>=RESERVED_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - } - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - break; - - case PAGER_WRITER_DBMOD: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( p->eLock>=EXCLUSIVE_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - assert( pPager->dbOrigSize<=pPager->dbHintSize ); - break; - - case PAGER_WRITER_FINISHED: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - break; - - case PAGER_ERROR: - /* There must be at least one outstanding reference to the pager if - ** in ERROR state. Otherwise the pager should have already dropped - ** back to OPEN state. - */ - assert( pPager->errCode!=SQLITE_OK ); - assert( sqlite4PcacheRefCount(pPager->pPCache)>0 ); - break; - } - - 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 -** is intended to be used within debuggers. For example, as an alternative -** to "print *pPager" in gdb: -** -** (gdb) printf "%s", print_pager_state(pPager) -*/ -static char *print_pager_state(Pager *p){ - static char zRet[1024]; - - sqlite4_snprintf(1024, zRet, - "Filename: %s\n" - "State: %s errCode=%d\n" - "Lock: %s\n" - "Locking mode: locking_mode=%s\n" - "Journal mode: journal_mode=%s\n" - "Backing store: tempFile=%d memDb=%d useJournal=%d\n" - "Journal: journalOff=%lld journalHdr=%lld\n" - "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n" - , p->zFilename - , p->eState==PAGER_OPEN ? "OPEN" : - p->eState==PAGER_READER ? "READER" : - p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" : - p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" : - p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" : - p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : - p->eState==PAGER_ERROR ? "ERROR" : "?error?" - , (int)p->errCode - , p->eLock==NO_LOCK ? "NO_LOCK" : - p->eLock==RESERVED_LOCK ? "RESERVED" : - p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" : - p->eLock==SHARED_LOCK ? "SHARED" : - p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?" - , p->exclusiveMode ? "exclusive" : "normal" - , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" : - p->journalMode==PAGER_JOURNALMODE_OFF ? "off" : - p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" : - p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" : - p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" : - p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?" - , (int)p->tempFile, (int)p->memDb, (int)p->useJournal - , p->journalOff, p->journalHdr - , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize - ); - - return zRet; -} -#endif - -/* -** Return true if it is necessary to write page *pPg into the sub-journal. -** A page needs to be written into the sub-journal if there exists one -** or more open savepoints for which: -** -** * The page-number is less than or equal to PagerSavepoint.nOrig, and -** * The bit corresponding to the page-number is not set in -** PagerSavepoint.pInSavepoint. -*/ -static int subjRequiresPage(PgHdr *pPg){ - Pgno pgno = pPg->pgno; - Pager *pPager = pPg->pPager; - int i; - for(i=0; inSavepoint; i++){ - PagerSavepoint *p = &pPager->aSavepoint[i]; - if( p->nOrig>=pgno && 0==sqlite4BitvecTest(p->pInSavepoint, pgno) ){ - return 1; - } - } - return 0; -} - -/* -** Return true if the page is already in the journal file. -*/ -static int pageInJournal(PgHdr *pPg){ - return sqlite4BitvecTest(pPg->pPager->pInJournal, pPg->pgno); -} - -/* -** Read a 32-bit integer from the given file descriptor. Store the integer -** that is read in *pRes. Return SQLITE_OK if everything worked, or an -** error code is something goes wrong. -** -** All values are stored on disk as big-endian. -*/ -static int read32bits(sqlite4_file *fd, i64 offset, u32 *pRes){ - unsigned char ac[4]; - int rc = sqlite4OsRead(fd, ac, sizeof(ac), offset); - if( rc==SQLITE_OK ){ - *pRes = sqlite4Get4byte(ac); - } - return rc; -} - -/* -** Write a 32-bit integer into a string buffer in big-endian byte order. -*/ -#define put32bits(A,B) sqlite4Put4byte((u8*)A,B) - - -/* -** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK -** on success or an error code is something goes wrong. -*/ -static int write32bits(sqlite4_file *fd, i64 offset, u32 val){ - char ac[4]; - put32bits(ac, val); - return sqlite4OsWrite(fd, ac, 4, offset); -} - -/* -** Unlock the database file to level eLock, which must be either NO_LOCK -** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() -** succeeds, set the Pager.eLock variable to match the (attempted) new lock. -** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it. See the comment above the #define of -** UNKNOWN_LOCK for an explanation of this. -*/ -static int pagerUnlockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; - - assert( !pPager->exclusiveMode || pPager->eLock==eLock ); - assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); - assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); - if( isOpen(pPager->fd) ){ - assert( pPager->eLock>=eLock ); - rc = sqlite4OsUnlock(pPager->fd, eLock); - if( pPager->eLock!=UNKNOWN_LOCK ){ - pPager->eLock = (u8)eLock; - } - IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) - } - return rc; -} - -/* -** Lock the database file to level eLock, which must be either SHARED_LOCK, -** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the -** Pager.eLock variable to the new locking state. -** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. -** See the comment above the #define of UNKNOWN_LOCK for an explanation -** of this. -*/ -static int pagerLockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; - - assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); - if( pPager->eLockeLock==UNKNOWN_LOCK ){ - rc = sqlite4OsLock(pPager->fd, eLock); - if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ - pPager->eLock = (u8)eLock; - IOTRACE(("LOCK %p %d\n", pPager, eLock)) - } - } - return rc; -} - -/* -** This function determines whether or not the atomic-write optimization -** can be used with this pager. The optimization can be used if: -** -** (a) the value returned by OsDeviceCharacteristics() indicates that -** a database page may be written atomically, and -** (b) the value returned by OsSectorSize() is less than or equal -** to the page size. -** -** The optimization is also always enabled for temporary files. It is -** an error to call this function if pPager is opened on an in-memory -** database. -** -** If the optimization cannot be used, 0 is returned. If it can be used, -** then the value returned is the size of the journal file when it -** contains rollback data for exactly one page. -*/ -#ifdef SQLITE_ENABLE_ATOMIC_WRITE -static int jrnlBufferSize(Pager *pPager){ - assert( !MEMDB ); - if( !pPager->tempFile ){ - int dc; /* Device characteristics */ - int nSector; /* Sector size */ - int szPage; /* Page size */ - - assert( isOpen(pPager->fd) ); - dc = sqlite4OsDeviceCharacteristics(pPager->fd); - nSector = pPager->sectorSize; - szPage = pPager->pageSize; - - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ - return 0; - } - } - - return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); -} -#endif - -/* -** If SQLITE_CHECK_PAGES is defined then we do some sanity checking -** on the cache using a hash function. This is used for testing -** and debugging only. -*/ -#ifdef SQLITE_CHECK_PAGES -/* -** Return a 32-bit hash of the page data for pPage. -*/ -static u32 pager_datahash(int nByte, unsigned char *pData){ - u32 hash = 0; - int i; - for(i=0; ipPager->pageSize, (unsigned char *)pPage->pData); -} -static void pager_set_pagehash(PgHdr *pPage){ - pPage->pageHash = pager_pagehash(pPage); -} - -/* -** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES -** is defined, and NDEBUG is not defined, an assert() statement checks -** that the page is either dirty or still matches the calculated page-hash. -*/ -#define CHECK_PAGE(x) checkPage(x) -static void checkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - assert( pPager->eState!=PAGER_ERROR ); - assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); -} - -#else -#define pager_datahash(X,Y) 0 -#define pager_pagehash(X) 0 -#define pager_set_pagehash(X) -#define CHECK_PAGE(x) -#endif /* SQLITE_CHECK_PAGES */ - -/* -** When this is called the journal file for pager pPager must be open. -** This function attempts to read a master journal file name from the -** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeMasterJournal() for the format -** used to store a master journal file name at the end of a journal file. -** -** zMaster must point to a buffer of at least nMaster bytes allocated by -** the caller. This should be sqlite4_vfs.mxPathname+1 (to ensure there is -** enough space to write the master journal name). If the master journal -** name in the journal is longer than nMaster bytes (including a -** nul-terminator), then this is handled as if no master journal name -** were present in the journal. -** -** If a master journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zMaster. A -** nul-terminator byte is appended to the buffer following the master -** journal file name. -** -** If it is determined that no master journal file name is present -** zMaster[0] is set to 0 and SQLITE_OK returned. -** -** If an error occurs while reading from the journal file, an SQLite -** error code is returned. -*/ -static int readMasterJournal(sqlite4_file *pJrnl, char *zMaster, u32 nMaster){ - int rc; /* Return code */ - u32 len; /* Length in bytes of master journal name */ - i64 szJ; /* Total size in bytes of journal file pJrnl */ - u32 cksum; /* MJ checksum value read from journal */ - u32 u; /* Unsigned loop counter */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - zMaster[0] = '\0'; - - if( SQLITE_OK!=(rc = sqlite4OsFileSize(pJrnl, &szJ)) - || szJ<16 - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) - || len>=nMaster - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) - || SQLITE_OK!=(rc = sqlite4OsRead(pJrnl, aMagic, 8, szJ-8)) - || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlite4OsRead(pJrnl, zMaster, len, szJ-16-len)) - ){ - return rc; - } - - /* See if the checksum matches the master journal name */ - for(u=0; ujournalOff, assuming a sector -** size of pPager->sectorSize bytes. -** -** i.e for a sector size of 512: -** -** Pager.journalOff Return value -** --------------------------------------- -** 0 0 -** 512 512 -** 100 512 -** 2000 2048 -** -*/ -static i64 journalHdrOffset(Pager *pPager){ - i64 offset = 0; - i64 c = pPager->journalOff; - if( c ){ - offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager); - } - assert( offset%JOURNAL_HDR_SZ(pPager)==0 ); - assert( offset>=c ); - assert( (offset-c)jfd) ); - if( pPager->journalOff ){ - const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ - - IOTRACE(("JZEROHDR %p\n", pPager)) - if( doTruncate || iLimit==0 ){ - rc = sqlite4OsTruncate(pPager->jfd, 0); - }else{ - static const char zeroHdr[28] = {0}; - rc = sqlite4OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); - } - if( rc==SQLITE_OK && !pPager->noSync ){ - rc = sqlite4OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags); - } - - /* At this point the transaction is committed but the write lock - ** is still held on the file. If there is a size limit configured for - ** the persistent journal and the journal file currently consumes more - ** space than that limit allows for, truncate it now. There is no need - ** to sync the file following this operation. - */ - if( rc==SQLITE_OK && iLimit>0 ){ - i64 sz; - rc = sqlite4OsFileSize(pPager->jfd, &sz); - if( rc==SQLITE_OK && sz>iLimit ){ - rc = sqlite4OsTruncate(pPager->jfd, iLimit); - } - } - } - return rc; -} - -/* -** The journal file must be open when this routine is called. A journal -** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the -** current location. -** -** The format for the journal header is as follows: -** - 8 bytes: Magic identifying journal format. -** - 4 bytes: Number of records in journal, or -1 no-sync mode is on. -** - 4 bytes: Random number used for page hash. -** - 4 bytes: Initial database page count. -** - 4 bytes: Sector size used by the process that wrote this journal. -** - 4 bytes: Database page size. -** -** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. -*/ -static int writeJournalHdr(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ - u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */ - u32 nWrite; /* Bytes of header sector written */ - int ii; /* Loop counter */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ - - if( nHeader>JOURNAL_HDR_SZ(pPager) ){ - nHeader = JOURNAL_HDR_SZ(pPager); - } - - /* If there are active savepoints and any of them were created - ** since the most recent journal header was written, update the - ** PagerSavepoint.iHdrOffset fields now. - */ - for(ii=0; iinSavepoint; ii++){ - if( pPager->aSavepoint[ii].iHdrOffset==0 ){ - pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff; - } - } - - pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); - - /* - ** Write the nRec Field - the number of page records that follow this - ** journal header. Normally, zero is written to this value at this time. - ** After the records are added to the journal (and the journal synced, - ** if in full-sync mode), the zero is overwritten with the true number - ** of records (see syncJournal()). - ** - ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When - ** reading the journal this value tells SQLite to assume that the - ** rest of the journal file contains valid page records. This assumption - ** is dangerous, as if a failure occurred whilst writing to the journal - ** file it may contain some garbage data. There are two scenarios - ** where this risk can be ignored: - ** - ** * When the pager is in no-sync mode. Corruption can follow a - ** power failure in this case anyway. - ** - ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees - ** that garbage data is never appended to the journal file. - */ - assert( isOpen(pPager->fd) || pPager->noSync ); - if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) - || (sqlite4OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) - ){ - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); - }else{ - memset(zHeader, 0, sizeof(aJournalMagic)+4); - } - - /* The random check-hash initialiser */ - sqlite4_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); - put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); - /* The initial database size */ - put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); - /* The assumed sector size for this process */ - put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); - - /* The page size */ - put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); - - /* Initializing the tail of the buffer is not necessary. Everything - ** works find if the following memset() is omitted. But initializing - ** the memory prevents valgrind from complaining, so we are willing to - ** take the performance hit. - */ - memset(&zHeader[sizeof(aJournalMagic)+20], 0, - nHeader-(sizeof(aJournalMagic)+20)); - - /* In theory, it is only necessary to write the 28 bytes that the - ** journal header consumes to the journal file here. Then increment the - ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next - ** record is written to the following sector (leaving a gap in the file - ** that will be implicitly filled in by the OS). - ** - ** However it has been discovered that on some systems this pattern can - ** be significantly slower than contiguously writing data to the file, - ** even if that means explicitly writing data to the block of - ** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what - ** is done. - ** - ** The loop is required here in case the sector-size is larger than the - ** database page size. Since the zHeader buffer is only Pager.pageSize - ** bytes in size, more than one call to sqlite4OsWrite() may be required - ** to populate the entire journal header sector. - */ - for(nWrite=0; rc==SQLITE_OK&&nWritejournalHdr, nHeader)) - rc = sqlite4OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); - assert( pPager->journalHdr <= pPager->journalOff ); - pPager->journalOff += nHeader; - } - - return rc; -} - -/* -** The journal file must be open when this is called. A journal header file -** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal -** file. The current location in the journal file is given by -** pPager->journalOff. See comments above function writeJournalHdr() for -** a description of the journal header format. -** -** If the header is read successfully, *pNRec is set to the number of -** page records following this header and *pDbSize is set to the size of the -** database before the transaction began, in pages. Also, pPager->cksumInit -** is set to the value read from the journal header. SQLITE_OK is returned -** in this case. -** -** If the journal header file appears to be corrupted, SQLITE_DONE is -** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes -** cannot be read from the journal file an error code is returned. -*/ -static int readJournalHdr( - Pager *pPager, /* Pager object */ - int isHot, - i64 journalSize, /* Size of the open journal file in bytes */ - u32 *pNRec, /* OUT: Value read from the nRec field */ - u32 *pDbSize /* OUT: Value of original database size field */ -){ - int rc; /* Return code */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - i64 iHdrOff; /* Offset of journal header being read */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ - - /* Advance Pager.journalOff to the start of the next sector. If the - ** journal file is too small for there to be a header stored at this - ** point, return SQLITE_DONE. - */ - pPager->journalOff = journalHdrOffset(pPager); - if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ - return SQLITE_DONE; - } - iHdrOff = pPager->journalOff; - - /* Read in the first 8 bytes of the journal header. If they do not match - ** the magic string found at the start of each journal header, return - ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, - ** proceed. - */ - if( isHot || iHdrOff!=pPager->journalHdr ){ - rc = sqlite4OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); - if( rc ){ - return rc; - } - if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ - return SQLITE_DONE; - } - } - - /* Read the first three 32-bit fields of the journal header: The nRec - ** field, the checksum-initializer and the database size at the start - ** of the transaction. Return an error code if anything goes wrong. - */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+12, &pPager->cksumInit)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+16, pDbSize)) - ){ - return rc; - } - - if( pPager->journalOff==0 ){ - u32 iPageSize; /* Page-size field of journal header */ - u32 iSectorSize; /* Sector-size field of journal header */ - - /* Read the page-size and sector-size journal header fields. */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) - ){ - return rc; - } - - /* Versions of SQLite prior to 3.5.8 set the page-size field of the - ** journal header to zero. In this case, assume that the Pager.pageSize - ** variable is already set to the correct page size. - */ - if( iPageSize==0 ){ - iPageSize = pPager->pageSize; - } - - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their - ** respective compile time maximum limits. - */ - if( iPageSize<512 || iSectorSize<32 - || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 - ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading - ** the journal file here. - */ - return SQLITE_DONE; - } - - /* Update the page-size to match the value read from the journal. - ** Use a testcase() macro to make sure that malloc failure within - ** PagerSetPagesize() is tested. - */ - rc = sqlite4PagerSetPagesize(pPager, &iPageSize, -1); - testcase( rc!=SQLITE_OK ); - - /* Update the assumed sector-size to match the value used by - ** the process that created this journal. If this journal was - ** created by a process other than this one, then this routine - ** is being called from within pager_playback(). The local value - ** of Pager.sectorSize is restored at the end of that routine. - */ - pPager->sectorSize = iSectorSize; - } - - pPager->journalOff += JOURNAL_HDR_SZ(pPager); - return rc; -} - - -/* -** Write the supplied master journal name into the journal file for pager -** pPager at the current location. The master journal name must be the last -** thing written to a journal file. If the pager is in full-sync mode, the -** journal file descriptor is advanced to the next sector boundary before -** anything is written. The format is: -** -** + 4 bytes: PAGER_MJ_PGNO. -** + N bytes: Master journal filename in utf-8. -** + 4 bytes: N (length of master journal name in bytes, no nul-terminator). -** + 4 bytes: Master journal name checksum. -** + 8 bytes: aJournalMagic[]. -** -** The master journal page checksum is the sum of the bytes in the master -** journal name, where each byte is interpreted as a signed 8-bit integer. -** -** If zMaster is a NULL pointer (occurs for a single database transaction), -** this call is a no-op. -*/ -static int writeMasterJournal(Pager *pPager, const char *zMaster){ - int rc; /* Return code */ - int nMaster; /* Length of string zMaster */ - i64 iHdrOff; /* Offset of header in journal file */ - i64 jrnlSize; /* Size of journal file on disk */ - u32 cksum = 0; /* Checksum of string zMaster */ - - assert( pPager->setMaster==0 ); - assert( !pagerUseWal(pPager) ); - - if( !zMaster - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_OFF - ){ - return SQLITE_OK; - } - pPager->setMaster = 1; - assert( isOpen(pPager->jfd) ); - assert( pPager->journalHdr <= pPager->journalOff ); - - /* Calculate the length in bytes and the checksum of zMaster */ - for(nMaster=0; zMaster[nMaster]; nMaster++){ - cksum += zMaster[nMaster]; - } - - /* If in full-sync mode, advance to the next disk sector before writing - ** the master journal name. This is in case the previous page written to - ** the journal has already been synced. - */ - if( pPager->fullSync ){ - pPager->journalOff = journalHdrOffset(pPager); - } - iHdrOff = pPager->journalOff; - - /* Write the master journal data to the end of the journal file. If - ** an error occurs, return the error code to the caller. - */ - if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) - || (0 != (rc = sqlite4OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum))) - || (0 != (rc = sqlite4OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8))) - ){ - return rc; - } - pPager->journalOff += (nMaster+20); - - /* If the pager is in peristent-journal mode, then the physical - ** journal-file may extend past the end of the master-journal name - ** and 8 bytes of magic data just written to the file. This is - ** dangerous because the code to rollback a hot-journal file - ** will not be able to find the master-journal name to determine - ** whether or not the journal is hot. - ** - ** Easiest thing to do in this scenario is to truncate the journal - ** file to the required size. - */ - if( SQLITE_OK==(rc = sqlite4OsFileSize(pPager->jfd, &jrnlSize)) - && jrnlSize>pPager->journalOff - ){ - rc = sqlite4OsTruncate(pPager->jfd, pPager->journalOff); - } - return rc; -} - -/* -** Find a page in the hash table given its page number. Return -** a pointer to the page or NULL if the requested page is not -** already in memory. -*/ -static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *p; /* Return value */ - - /* It is not possible for a call to PcacheFetch() with createFlag==0 to - ** fail, since no attempt to allocate dynamic memory will be made. - */ - (void)sqlite4PcacheFetch(pPager->pPCache, pgno, 0, &p); - return p; -} - -/* -** Discard the entire contents of the in-memory page-cache. -*/ -static void pager_reset(Pager *pPager){ - sqlite4BackupRestart(pPager->pBackup); - sqlite4PcacheClear(pPager->pPCache); -} - -/* -** Free all structures in the Pager.aSavepoint[] array and set both -** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal -** if it is open and the pager is not in exclusive mode. -*/ -static void releaseAllSavepoints(Pager *pPager){ - int ii; /* Iterator for looping through Pager.aSavepoint */ - for(ii=0; iinSavepoint; ii++){ - sqlite4BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - if( !pPager->exclusiveMode || sqlite4IsMemJournal(pPager->sjfd) ){ - sqlite4OsClose(pPager->sjfd); - } - sqlite4_free(pPager->aSavepoint); - pPager->aSavepoint = 0; - pPager->nSavepoint = 0; - pPager->nSubRec = 0; -} - -/* -** Set the bit number pgno in the PagerSavepoint.pInSavepoint -** bitvecs of all open savepoints. Return SQLITE_OK if successful -** or SQLITE_NOMEM if a malloc failure occurs. -*/ -static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ - int ii; /* Loop counter */ - int rc = SQLITE_OK; /* Result code */ - - for(ii=0; iinSavepoint; ii++){ - PagerSavepoint *p = &pPager->aSavepoint[ii]; - if( pgno<=p->nOrig ){ - rc |= sqlite4BitvecSet(p->pInSavepoint, pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - } - } - return rc; -} - -/* -** This function is a no-op if the pager is in exclusive mode and not -** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN -** state. -** -** If the pager is not in exclusive-access mode, the database file is -** completely unlocked. If the file is unlocked and the file-system does -** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is -** closed (if it is open). -** -** If the pager is in ERROR state when this function is called, the -** contents of the pager cache are discarded before switching back to -** the OPEN state. Regardless of whether the pager is in exclusive-mode -** or not, any journal file left in the file-system will be treated -** as a hot-journal and rolled back the next time a read-transaction -** is opened (by this or by any other connection). -*/ -static void pager_unlock(Pager *pPager){ - - assert( pPager->eState==PAGER_READER - || pPager->eState==PAGER_OPEN - || pPager->eState==PAGER_ERROR - ); - - sqlite4BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - releaseAllSavepoints(pPager); - - if( !pPager->exclusiveMode ){ - int rc; /* Error code returned by pagerUnlockDb() */ - int iDc = isOpen(pPager->fd)?sqlite4OsDeviceCharacteristics(pPager->fd):0; - - /* If the operating system support deletion of open files, then - ** close the journal file when dropping the database lock. Otherwise - ** another connection with journal_mode=delete might delete the file - ** out from under us. - */ - assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); - assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); - assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) - || 1!=(pPager->journalMode & 5) - ){ - sqlite4OsClose(pPager->jfd); - } - - /* If the pager is in the ERROR state and the call to unlock the database - ** file fails, set the current lock to UNKNOWN_LOCK. See the comment - ** above the #define for UNKNOWN_LOCK for an explanation of why this - ** is necessary. - */ - rc = pagerUnlockDb(pPager, NO_LOCK); - if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ - pPager->eLock = UNKNOWN_LOCK; - } - - /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here - ** without clearing the error code. This is intentional - the error - ** code is cleared and the cache reset in the block below. - */ - assert( pPager->errCode || pPager->eState!=PAGER_ERROR ); - pPager->changeCountDone = 0; - pPager->eState = PAGER_OPEN; - } - - /* If Pager.errCode is set, the contents of the pager cache cannot be - ** trusted. Now that there are no outstanding references to the pager, - ** it can safely move back to PAGER_OPEN state. This happens in both - ** normal and exclusive-locking mode. - */ - if( pPager->errCode ){ - assert( !MEMDB ); - pager_reset(pPager); - pPager->changeCountDone = pPager->tempFile; - pPager->eState = PAGER_OPEN; - pPager->errCode = SQLITE_OK; - } - - pPager->journalOff = 0; - pPager->journalHdr = 0; - pPager->setMaster = 0; -} - -/* -** This function is called whenever an IOERR or FULL error that requires -** the pager to transition into the ERROR state may ahve occurred. -** The first argument is a pointer to the pager structure, the second -** the error-code about to be returned by a pager API function. The -** value returned is a copy of the second argument to this function. -** -** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the -** IOERR sub-codes, the pager enters the ERROR state and the error code -** is stored in Pager.errCode. While the pager remains in the ERROR state, -** all major API calls on the Pager will immediately return Pager.errCode. -** -** The ERROR state indicates that the contents of the pager-cache -** cannot be trusted. This state can be cleared by completely discarding -** the contents of the pager-cache. If a transaction was active when -** the persistent error occurred, then the rollback journal may need -** to be replayed to restore the contents of the database file (as if -** it were a hot-journal). -*/ -static int pager_error(Pager *pPager, int rc){ - int rc2 = rc & 0xff; - assert( rc==SQLITE_OK || !MEMDB ); - assert( - pPager->errCode==SQLITE_FULL || - pPager->errCode==SQLITE_OK || - (pPager->errCode & 0xff)==SQLITE_IOERR - ); - if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ - pPager->errCode = rc; - pPager->eState = PAGER_ERROR; - } - return rc; -} - -/* -** This routine ends a transaction. A transaction is usually ended by -** either a COMMIT or a ROLLBACK operation. This routine may be called -** after rollback of a hot-journal, or if an error occurs while opening -** the journal file or writing the very first journal-header of a -** database transaction. -** -** This routine is never called in PAGER_ERROR state. If it is called -** in PAGER_NONE or PAGER_SHARED state and the lock held is less -** exclusive than a RESERVED lock, it is a no-op. -** -** Otherwise, any active savepoints are released. -** -** If the journal file is open, then it is "finalized". Once a journal -** file has been finalized it is not possible to use it to roll back a -** transaction. Nor will it be considered to be a hot-journal by this -** or any other database connection. Exactly how a journal is finalized -** depends on whether or not the pager is running in exclusive mode and -** the current journal-mode (Pager.journalMode value), as follows: -** -** journalMode==MEMORY -** Journal file descriptor is simply closed. This destroys an -** in-memory journal. -** -** journalMode==TRUNCATE -** Journal file is truncated to zero bytes in size. -** -** journalMode==PERSIST -** The first 28 bytes of the journal file are zeroed. This invalidates -** the first journal header in the file, and hence the entire journal -** file. An invalid journal file cannot be rolled back. -** -** journalMode==DELETE -** The journal file is closed and deleted using sqlite4OsDelete(). -** -** If the pager is running in exclusive mode, this method of finalizing -** the journal file is never used. Instead, if the journalMode is -** DELETE and the pager is in exclusive mode, the method described under -** journalMode==PERSIST is used instead. -** -** After the journal is finalized, the pager moves to PAGER_READER state. -** If running in non-exclusive rollback mode, the lock on the file is -** downgraded to a SHARED_LOCK. -** -** SQLITE_OK is returned if no error occurs. If an error occurs during -** any of the IO operations to finalize the journal file or unlock the -** database then the IO error code is returned to the user. If the -** operation to finalize the journal file fails, then the code still -** tries to unlock the database file if not in exclusive mode. If the -** unlock operation fails as well, then the first error code related -** to the first error encountered (the journal finalization one) is -** returned. -*/ -static int pager_end_transaction(Pager *pPager, int hasMaster){ - int rc = SQLITE_OK; /* Error code from journal finalization operation */ - int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ - - /* Do nothing if the pager does not have an open write transaction - ** or at least a RESERVED lock. This function may be called when there - ** is no write-transaction active but a RESERVED or greater lock is - ** held under two circumstances: - ** - ** 1. After a successful hot-journal rollback, it is called with - ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK. - ** - ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE - ** lock switches back to locking_mode=normal and then executes a - ** read-transaction, this function is called with eState==PAGER_READER - ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed. - */ - assert( assert_pager_state(pPager) ); - assert( pPager->eState!=PAGER_ERROR ); - if( pPager->eStateeLockjfd) || pPager->pInJournal==0 ); - if( isOpen(pPager->jfd) ){ - assert( !pagerUseWal(pPager) ); - - /* Finalize the journal file. */ - if( sqlite4IsMemJournal(pPager->jfd) ){ - assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); - sqlite4OsClose(pPager->jfd); - }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ - if( pPager->journalOff==0 ){ - rc = SQLITE_OK; - }else{ - rc = sqlite4OsTruncate(pPager->jfd, 0); - } - pPager->journalOff = 0; - }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST - || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) - ){ - rc = zeroJournalHdr(pPager, hasMaster); - pPager->journalOff = 0; - }else{ - /* This branch may be executed with Pager.journalMode==MEMORY if - ** a hot-journal was just rolled back. In this case the journal - ** file should be closed and deleted. If this connection writes to - ** the database file, it will do so using an in-memory journal. - */ - assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_WAL - ); - sqlite4OsClose(pPager->jfd); - if( !pPager->tempFile ){ - rc = sqlite4OsDelete(pPager->pVfs, pPager->zJournal, 0); - } - } - } - -#ifdef SQLITE_CHECK_PAGES - sqlite4PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); - if( pPager->dbSize==0 && sqlite4PcacheRefCount(pPager->pPCache)>0 ){ - PgHdr *p = pager_lookup(pPager, 1); - if( p ){ - p->pageHash = 0; - sqlite4PagerUnref(p); - } - } -#endif - - sqlite4BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - pPager->nRec = 0; - sqlite4PcacheCleanAll(pPager->pPCache); - sqlite4PcacheTruncate(pPager->pPCache, pPager->dbSize); - - if( !pPager->exclusiveMode ){ - rc2 = pagerUnlockDb(pPager, SHARED_LOCK); - pPager->changeCountDone = 0; - } - pPager->eState = PAGER_READER; - pPager->setMaster = 0; - - return (rc==SQLITE_OK?rc2:rc); -} - -/* -** Execute a rollback if a transaction is active and unlock the -** database file. -** -** If the pager has already entered the ERROR state, do not attempt -** the rollback at this time. Instead, pager_unlock() is called. The -** call to pager_unlock() will discard all in-memory pages, unlock -** the database file and move the pager back to OPEN state. If this -** means that there is a hot-journal left in the file-system, the next -** connection to obtain a shared lock on the pager (which may be this one) -** will roll it back. -** -** If the pager has not already entered the ERROR state, but an IO or -** malloc error occurs during a rollback, then this will itself cause -** the pager to enter the ERROR state. Which will be cleared by the -** call to pager_unlock(), as described above. -*/ -static void pagerUnlockAndRollback(Pager *pPager){ - if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_LOCKED ){ - sqlite4BeginBenignMalloc(); - sqlite4PagerRollback(pPager); - sqlite4EndBenignMalloc(); - }else if( !pPager->exclusiveMode ){ - assert( pPager->eState==PAGER_READER ); - pager_end_transaction(pPager, 0); - } - } - pager_unlock(pPager); -} - -/* -** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the -** page of data and the current value of pPager->cksumInit. -** -** This is not a real checksum. It is really just the sum of the -** random initial value (pPager->cksumInit) and every 200th byte -** of the page data, starting with byte offset (pPager->pageSize%200). -** Each byte is interpreted as an 8-bit unsigned integer. -** -** Changing the formula used to compute this checksum results in an -** incompatible journal file format. -** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. -** It is much less likely that the two ends of the journal record will be -** correct and the middle be corrupt. Thus, this "checksum" scheme, -** though fast and simple, catches the mostly likely kind of corruption. -*/ -static u32 pager_cksum(Pager *pPager, const u8 *aData){ - u32 cksum = pPager->cksumInit; /* Checksum value to return */ - int i = pPager->pageSize-200; /* Loop counter */ - while( i>0 ){ - cksum += aData[i]; - i -= 200; - } - return cksum; -} - -/* -** Report the current page size and number of reserved bytes back -** to the codec. -*/ -#ifdef SQLITE_HAS_CODEC -static void pagerReportSize(Pager *pPager){ - if( pPager->xCodecSizeChng ){ - pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, - (int)pPager->nReserve); - } -} -#else -# define pagerReportSize(X) /* No-op if we do not support a codec */ -#endif - -/* -** Read a single page from either the journal file (if isMainJrnl==1) or -** from the sub-journal (if isMainJrnl==0) and playback that page. -** The page begins at offset *pOffset into the file. The *pOffset -** value is increased to the start of the next page in the journal. -** -** The main rollback journal uses checksums - the statement journal does -** not. -** -** If the page number of the page record read from the (sub-)journal file -** is greater than the current value of Pager.dbSize, then playback is -** skipped and SQLITE_OK is returned. -** -** If pDone is not NULL, then it is a record of pages that have already -** been played back. If the page at *pOffset has already been played back -** (if the corresponding pDone bit is set) then skip the playback. -** Make sure the pDone bit corresponding to the *pOffset page is set -** prior to returning. -** -** If the page record is successfully read from the (sub-)journal file -** and played back, then SQLITE_OK is returned. If an IO error occurs -** while reading the record from the (sub-)journal file or while writing -** to the database file, then the IO error code is returned. If data -** is successfully read from the (sub-)journal file but appears to be -** corrupted, SQLITE_DONE is returned. Data is considered corrupted in -** two circumstances: -** -** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or -** * If the record is being rolled back from the main journal file -** and the checksum field does not match the record content. -** -** Neither of these two scenarios are possible during a savepoint rollback. -** -** If this is a savepoint rollback, then memory may have to be dynamically -** allocated by this function. If this is the case and an allocation fails, -** SQLITE_NOMEM is returned. -*/ -static int pager_playback_one_page( - Pager *pPager, /* The pager being played back */ - i64 *pOffset, /* Offset of record to playback */ - Bitvec *pDone, /* Bitvec of pages already played back */ - int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ - int isSavepnt /* True for a savepoint rollback */ -){ - int rc; - PgHdr *pPg; /* An existing page in the cache */ - Pgno pgno; /* The page number of a page in journal */ - u32 cksum; /* Checksum used for sanity checking */ - char *aData; /* Temporary storage for the page */ - sqlite4_file *jfd; /* The file descriptor for the journal file */ - int isSynced; /* True if journal page is synced */ - - assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ - assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ - assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ - assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ - - aData = pPager->pTmpSpace; - assert( aData ); /* Temp storage must have already been allocated */ - assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); - - /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction - ** or savepoint rollback done at the request of the caller) or this is - ** a hot-journal rollback. If it is a hot-journal rollback, the pager - ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback - ** only reads from the main journal, not the sub-journal. - */ - assert( pPager->eState>=PAGER_WRITER_CACHEMOD - || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK) - ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl ); - - /* Read the page number and page data from the journal or sub-journal - ** file. Return an error code to the caller if an IO error occurs. - */ - jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; - rc = read32bits(jfd, *pOffset, &pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite4OsRead(jfd, (u8*)aData, pPager->pageSize, (*pOffset)+4); - if( rc!=SQLITE_OK ) return rc; - *pOffset += pPager->pageSize + 4 + isMainJrnl*4; - - /* Sanity checking on the page. This is more important that I originally - ** thought. If a power failure occurs while the journal is being written, - ** it could cause invalid data to be written into the journal. We need to - ** detect this invalid data (with high probability) and ignore it. - */ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ - assert( !isSavepnt ); - return SQLITE_DONE; - } - if( pgno>(Pgno)pPager->dbSize || sqlite4BitvecTest(pDone, pgno) ){ - return SQLITE_OK; - } - if( isMainJrnl ){ - rc = read32bits(jfd, (*pOffset)-4, &cksum); - if( rc ) return rc; - if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ - return SQLITE_DONE; - } - } - - /* If this page has already been played by before during the current - ** rollback, then don't bother to play it back again. - */ - if( pDone && (rc = sqlite4BitvecSet(pDone, pgno))!=SQLITE_OK ){ - return rc; - } - - /* When playing back page 1, restore the nReserve setting - */ - if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ - pPager->nReserve = ((u8*)aData)[20]; - pagerReportSize(pPager); - } - - /* If the pager is in CACHEMOD state, then there must be a copy of this - ** page in the pager cache. In this case just update the pager cache, - ** not the database file. The page is left marked dirty in this case. - ** - ** An exception to the above rule: If the database is in no-sync mode - ** and a page is moved during an incremental vacuum then the page may - ** not be in the pager cache. Later: if a malloc() or IO error occurs - ** during a Movepage() call, then the page may not be in the cache - ** either. So the condition described in the above paragraph is not - ** assert()able. - ** - ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the - ** pager cache if it exists and the main file. The page is then marked - ** not dirty. Since this code is only executed in PAGER_OPEN state for - ** a hot-journal rollback, it is guaranteed that the page-cache is empty - ** if the pager is in OPEN state. - ** - ** Ticket #1171: The statement journal might contain page content that is - ** different from the page content at the start of the transaction. - ** This occurs when a page is changed prior to the start of a statement - ** then changed again within the statement. When rolling back such a - ** statement we must not write to the original database unless we know - ** for certain that original page contents are synced into the main rollback - ** journal. Otherwise, a power loss might leave modified data in the - ** database file without an entry in the rollback journal that can - ** restore the database to its original form. Two conditions must be - ** met before writing to the database files. (1) the database must be - ** locked. (2) we know that the original page content is fully synced - ** in the main journal either because the page is not in cache or else - ** the page is marked as needSync==0. - ** - ** 2008-04-14: When attempting to vacuum a corrupt database file, it - ** is possible to fail a statement on a database that does not yet exist. - ** Do not attempt to write if database file has never been opened. - */ - if( pagerUseWal(pPager) ){ - pPg = 0; - }else{ - pPg = pager_lookup(pPager, pgno); - } - assert( pPg || !MEMDB ); - assert( pPager->eState!=PAGER_OPEN || pPg==0 ); - PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", - PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), - (isMainJrnl?"main-journal":"sub-journal") - )); - if( isMainJrnl ){ - isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); - }else{ - isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); - } - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - && isSynced - ){ - i64 ofst = (pgno-1)*(i64)pPager->pageSize; - testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); - assert( !pagerUseWal(pPager) ); - rc = sqlite4OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; - } - if( pPager->pBackup ){ - CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); - sqlite4BackupUpdate(pPager->pBackup, pgno, (u8*)aData); - CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData); - } - }else if( !isMainJrnl && pPg==0 ){ - /* If this is a rollback of a savepoint and data was not written to - ** the database and the page is not in-memory, there is a potential - ** problem. When the page is next fetched by the b-tree layer, it - ** will be read from the database file, which may or may not be - ** current. - ** - ** There are a couple of different ways this can happen. All are quite - ** obscure. When running in synchronous mode, this can only happen - ** if the page is on the free-list at the start of the transaction, then - ** populated, then moved using sqlite4PagerMovepage(). - ** - ** The solution is to add an in-memory page to the cache containing - ** the data just read from the sub-journal. Mark the page as dirty - ** and if the pager requires a journal-sync, then mark the page as - ** requiring a journal-sync before it is written. - */ - assert( isSavepnt ); - assert( pPager->doNotSpill==0 ); - pPager->doNotSpill++; - rc = sqlite4PagerAcquire(pPager, pgno, &pPg, 1); - assert( pPager->doNotSpill==1 ); - pPager->doNotSpill--; - if( rc!=SQLITE_OK ) return rc; - pPg->flags &= ~PGHDR_NEED_READ; - sqlite4PcacheMakeDirty(pPg); - } - if( pPg ){ - /* No page should ever be explicitly rolled back that is in use, except - ** for page 1 which is held in use in order to keep the lock on the - ** database active. However such a page may be rolled back as a result - ** of an internal error resulting in an automatic call to - ** sqlite4PagerRollback(). - */ - void *pData; - pData = pPg->pData; - memcpy(pData, (u8*)aData, pPager->pageSize); - pPager->xReiniter(pPg); - if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ - /* If the contents of this page were just restored from the main - ** journal file, then its content must be as they were when the - ** transaction was first opened. In this case we can mark the page - ** as clean, since there will be no need to write it out to the - ** database. - ** - ** There is one exception to this rule. If the page is being rolled - ** back as part of a savepoint (or statement) rollback from an - ** unsynced portion of the main journal file, then it is not safe - ** to mark the page as clean. This is because marking the page as - ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is - ** already in the journal file (recorded in Pager.pInJournal) and - ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to - ** again within this transaction, it will be marked as dirty but - ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially - ** be written out into the database file before its journal file - ** segment is synced. If a crash occurs during or following this, - ** database corruption may ensue. - */ - assert( !pagerUseWal(pPager) ); - sqlite4PcacheMakeClean(pPg); - } - pager_set_pagehash(pPg); - - /* If this was page 1, then restore the value of Pager.dbFileVers. - ** Do this before any decoding. */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); - } - - /* Decode the page just read from disk */ - CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM); - sqlite4PcacheRelease(pPg); - } - return rc; -} - -/* -** Parameter zMaster is the name of a master journal file. A single journal -** file that referred to the master journal file has just been rolled back. -** This routine checks if it is possible to delete the master journal file, -** and does so if it is. -** -** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not -** available for use within this function. -** -** When a master journal file is created, it is populated with the names -** of all of its child journals, one after another, formatted as utf-8 -** encoded text. The end of each child journal file is marked with a -** nul-terminator byte (0x00). i.e. the entire contents of a master journal -** file for a transaction involving two databases might be: -** -** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" -** -** A master journal file may only be deleted once all of its child -** journals have been rolled back. -** -** This function reads the contents of the master-journal file into -** memory and loops through each of the child journal names. For -** each child journal, it checks if: -** -** * if the child journal exists, and if so -** * if the child journal contains a reference to master journal -** file zMaster -** -** If a child journal can be found that matches both of the criteria -** above, this function returns without doing anything. Otherwise, if -** no such child journal can be found, file zMaster is deleted from -** the file-system using sqlite4OsDelete(). -** -** If an IO error within this function, an error code is returned. This -** function allocates memory by calling sqlite4Malloc(). If an allocation -** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors -** occur, SQLITE_OK is returned. -** -** TODO: This function allocates a single block of memory to load -** the entire contents of the master journal file. This could be -** a couple of kilobytes or so - potentially larger than the page -** size. -*/ -static int pager_delmaster(Pager *pPager, const char *zMaster){ - sqlite4_vfs *pVfs = pPager->pVfs; - int rc; /* Return code */ - sqlite4_file *pMaster; /* Malloc'd master-journal file descriptor */ - sqlite4_file *pJournal; /* Malloc'd child-journal file descriptor */ - char *zMasterJournal = 0; /* Contents of master journal file */ - i64 nMasterJournal; /* Size of master journal file */ - char *zJournal; /* Pointer to one journal within MJ file */ - char *zMasterPtr; /* Space to hold MJ filename from a journal file */ - int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */ - - /* Allocate space for both the pJournal and pMaster file descriptors. - ** If successful, open the master journal file for reading. - */ - pMaster = (sqlite4_file *)sqlite4MallocZero(pVfs->szOsFile * 2); - pJournal = (sqlite4_file *)(((u8 *)pMaster) + pVfs->szOsFile); - if( !pMaster ){ - rc = SQLITE_NOMEM; - }else{ - const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); - rc = sqlite4OsOpen(pVfs, zMaster, pMaster, flags, 0); - } - if( rc!=SQLITE_OK ) goto delmaster_out; - - /* Load the entire master journal file into space obtained from - ** sqlite4_malloc() and pointed to by zMasterJournal. Also obtain - ** sufficient space (in zMasterPtr) to hold the names of master - ** journal files extracted from regular rollback-journals. - */ - rc = sqlite4OsFileSize(pMaster, &nMasterJournal); - if( rc!=SQLITE_OK ) goto delmaster_out; - nMasterPtr = pVfs->mxPathname+1; - zMasterJournal = sqlite4Malloc((int)nMasterJournal + nMasterPtr + 1); - if( !zMasterJournal ){ - rc = SQLITE_NOMEM; - goto delmaster_out; - } - zMasterPtr = &zMasterJournal[nMasterJournal+1]; - rc = sqlite4OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); - if( rc!=SQLITE_OK ) goto delmaster_out; - zMasterJournal[nMasterJournal] = 0; - - zJournal = zMasterJournal; - while( (zJournal-zMasterJournal)pageSize bytes). -** If the file on disk is currently larger than nPage pages, then use the VFS -** xTruncate() method to truncate it. -** -** Or, it might might be the case that the file on disk is smaller than -** nPage pages. Some operating system implementations can get confused if -** you try to truncate a file to some size that is larger than it -** currently is, so detect this case and write a single zero byte to -** the end of the new file instead. -** -** If successful, return SQLITE_OK. If an IO error occurs while modifying -** the database file, return the error code to the caller. -*/ -static int pager_truncate(Pager *pPager, Pgno nPage){ - int rc = SQLITE_OK; - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState!=PAGER_READER ); - - if( isOpen(pPager->fd) - && (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 = sqlite4OsFileSize(pPager->fd, ¤tSize); - newSize = szPage*(i64)nPage; - if( rc==SQLITE_OK && currentSize!=newSize ){ - if( currentSize>newSize ){ - rc = sqlite4OsTruncate(pPager->fd, newSize); - }else if( (currentSize+szPage)<=newSize ){ - char *pTmp = pPager->pTmpSpace; - memset(pTmp, 0, szPage); - testcase( (newSize-szPage) == currentSize ); - testcase( (newSize-szPage) > currentSize ); - rc = sqlite4OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); - } - if( rc==SQLITE_OK ){ - pPager->dbFileSize = nPage; - } - } - } - return rc; -} - -/* -** Set the value of the Pager.sectorSize variable for the given -** pager based on the value returned by the xSectorSize method -** of the open database file. The sector size will be used used -** to determine the size and alignment of journal header and -** master journal pointers within created journal files. -** -** For temporary files the effective sector size is always 512 bytes. -** -** Otherwise, for non-temporary files, the effective sector size is -** the value returned by the xSectorSize() method rounded up to 32 if -** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it -** is greater than MAX_SECTOR_SIZE. -** -** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set -** the effective sector size to its minimum value (512). The purpose of -** pPager->sectorSize is to define the "blast radius" of bytes that -** might change if a crash occurs while writing to a single byte in -** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero -** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector -** size. For backwards compatibility of the rollback journal file format, -** we cannot reduce the effective sector size below 512. -*/ -static void setSectorSize(Pager *pPager){ - assert( isOpen(pPager->fd) || pPager->tempFile ); - - if( pPager->tempFile - || (sqlite4OsDeviceCharacteristics(pPager->fd) & - SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 - ){ - /* Sector size doesn't matter for temporary files. Also, the file - ** may not have been opened yet, in which case the OsSectorSize() - ** call will segfault. */ - pPager->sectorSize = 512; - }else{ - pPager->sectorSize = sqlite4OsSectorSize(pPager->fd); - if( pPager->sectorSize<32 ){ - pPager->sectorSize = 512; - } - if( pPager->sectorSize>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); - pPager->sectorSize = MAX_SECTOR_SIZE; - } - } -} - -/* -** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. -** -** The journal file format is as follows: -** -** (1) 8 byte prefix. A copy of aJournalMagic[]. -** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. If this value is 0xffffffff, then compute the -** number of page records from the journal size. -** (3) 4 byte big-endian integer which is the initial value for the -** sanity checksum. -** (4) 4 byte integer which is the number of pages to truncate the -** database to during a rollback. -** (5) 4 byte big-endian integer which is the sector size. The header -** is this many bytes in size. -** (6) 4 byte big-endian integer which is the page size. -** (7) zero padding out to the next sector size. -** (8) Zero or more pages instances, each as follows: -** + 4 byte page number. -** + pPager->pageSize bytes of data. -** + 4 byte checksum -** -** When we speak of the journal header, we mean the first 7 items above. -** Each entry in the journal is an instance of the 8th item. -** -** Call the value from the second bullet "nRec". nRec is the number of -** valid page entries in the journal. In most cases, you can compute the -** value of nRec from the size of the journal file. But if a power -** failure occurred while the journal was being written, it could be the -** case that the size of the journal file had already been increased but -** the extra entries had not yet made it safely to disk. In such a case, -** the value of nRec computed from the file size would be too large. For -** that reason, we always use the nRec value in the header. -** -** If the nRec value is 0xffffffff it means that nRec should be computed -** from the file size. This value is used when the user selects the -** no-sync option for the journal. A power failure could lead to corruption -** in this case. But for things like temporary table (which will be -** deleted when the power is restored) we don't care. -** -** If the file opened as the journal file is not a well-formed -** journal file then all pages up to the first corrupted page are rolled -** back (or no pages if the journal header is corrupted). The journal file -** is then deleted and SQLITE_OK returned, just as if no corruption had -** been encountered. -** -** If an I/O or malloc() error occurs, the journal-file is not deleted -** and an error code is returned. -** -** The isHot parameter indicates that we are trying to rollback a journal -** that might be a hot journal. Or, it could be that the journal is -** preserved because of JOURNALMODE_PERSIST or JOURNALMODE_TRUNCATE. -** If the journal really is hot, reset the pager cache prior rolling -** back any content. If the journal is merely persistent, no reset is -** needed. -*/ -static int pager_playback(Pager *pPager, int isHot){ - sqlite4_vfs *pVfs = pPager->pVfs; - i64 szJ; /* Size of the journal file in bytes */ - u32 nRec; /* Number of Records in the journal */ - u32 u; /* Unsigned loop counter */ - Pgno mxPg = 0; /* Size of the original file in pages */ - int rc; /* Result code of a subroutine */ - int res = 1; /* Value returned by sqlite4OsAccess() */ - char *zMaster = 0; /* Name of master journal file if any */ - int needPagerReset; /* True to reset page prior to first page rollback */ - - /* Figure out how many records are in the journal. Abort early if - ** the journal is empty. - */ - assert( isOpen(pPager->jfd) ); - rc = sqlite4OsFileSize(pPager->jfd, &szJ); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - - /* Read the master journal name from the journal, if it is present. - ** If a master journal file name is specified, but the file is not - ** present on disk, then the journal is not hot and does not need to be - ** played back. - ** - ** TODO: Technically the following is an error because it assumes that - ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize. - */ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - if( rc==SQLITE_OK && zMaster[0] ){ - rc = sqlite4OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); - } - zMaster = 0; - if( rc!=SQLITE_OK || !res ){ - goto end_playback; - } - pPager->journalOff = 0; - needPagerReset = isHot; - - /* This loop terminates either when a readJournalHdr() or - ** pager_playback_one_page() call returns SQLITE_DONE or an IO error - ** occurs. - */ - while( 1 ){ - /* Read the next journal header from the journal file. If there are - ** not enough bytes left in the journal file for a complete header, or - ** it is corrupted, then a process must have failed while writing it. - ** This indicates nothing more needs to be rolled back. - */ - rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - } - goto end_playback; - } - - /* If nRec is 0xffffffff, then this journal was created by a process - ** working in no-sync mode. This means that the rest of the journal - ** file consists of pages, there are no more journal headers. Compute - ** the value of nRec based on this assumption. - */ - if( nRec==0xffffffff ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); - nRec = (int)((szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager)); - } - - /* If nRec is 0 and this rollback is of a transaction created by this - ** process and if this is the final header in the journal, then it means - ** that this part of the journal was being filled but has not yet been - ** synced to disk. Compute the number of pages based on the remaining - ** size of the file. - ** - ** The third term of the test was added to fix ticket #2565. - ** When rolling back a hot journal, nRec==0 always means that the next - ** chunk of the journal contains zero pages to be rolled back. But - ** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in - ** the journal, it means that the journal might contain additional - ** pages that need to be rolled back and that the number of pages - ** should be computed based on the journal file size. - */ - if( nRec==0 && !isHot && - pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ - nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); - } - - /* If this is the first header read from the journal, truncate the - ** database file back to its original size. - */ - if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ - rc = pager_truncate(pPager, mxPg); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - pPager->dbSize = mxPg; - } - - /* Copy original pages out of the journal and back into the - ** database file and/or page cache. - */ - for(u=0; ujournalOff,0,1,0); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - pPager->journalOff = szJ; - break; - }else if( rc==SQLITE_IOERR_SHORT_READ ){ - /* If the journal has been truncated, simply stop reading and - ** processing the journal. This might happen if the journal was - ** not completely written and synced prior to a crash. In that - ** case, the database should have never been written in the - ** first place so it is OK to simply abandon the rollback. */ - rc = SQLITE_OK; - goto end_playback; - }else{ - /* If we are unable to rollback, quit and return the error - ** code. This will cause the pager to enter the error state - ** so that no further harm will be done. Perhaps the next - ** process to come along will be able to rollback the database. - */ - goto end_playback; - } - } - } - } - /*NOTREACHED*/ - assert( 0 ); - -end_playback: - /* Following a rollback, the database file should be back in its original - ** state prior to the start of the transaction, so invoke the - ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the - ** assertion that the transaction counter was modified. - */ -#ifdef SQLITE_DEBUG - if( pPager->fd->pMethods ){ - sqlite4OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); - } -#endif - - /* If this playback is happening automatically as a result of an IO or - ** malloc error that occurred after the change-counter was updated but - ** before the transaction was committed, then the change-counter - ** modification may just have been reverted. If this happens in exclusive - ** mode, then subsequent transactions performed by the connection will not - ** update the change-counter at all. This may lead to cache inconsistency - ** problems for other processes at some point in the future. So, just - ** in case this has happened, clear the changeCountDone flag now. - */ - pPager->changeCountDone = pPager->tempFile; - - if( rc==SQLITE_OK ){ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - ){ - rc = sqlite4PagerSync(pPager); - } - if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zMaster[0]!='\0'); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK && zMaster[0] && res ){ - /* If there was a master journal and this routine will return success, - ** see if it is possible to delete the master journal. - */ - rc = pager_delmaster(pPager, zMaster); - testcase( rc!=SQLITE_OK ); - } - - /* The Pager.sectorSize variable may have been updated while rolling - ** back a journal created by a process with a different sector size - ** value. Reset it to the correct value for this process. - */ - setSectorSize(pPager); - return rc; -} - - -/* -** Read the content for page pPg out of the database file and into -** pPg->pData. A shared lock or greater must be held on the database -** file before this function is called. -** -** If page 1 is read, then the value of Pager.dbFileVers[] is set to -** the value read from the database file. -** -** If an IO error occurs, then the IO error is returned to the caller. -** Otherwise, SQLITE_OK is returned. -*/ -static int readDbPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ - Pgno pgno = pPg->pgno; /* Page number to read */ - int rc = SQLITE_OK; /* Return code */ - int isInWal = 0; /* True if page is in log file */ - int pgsz = pPager->pageSize; /* Number of bytes to read */ - - assert( pPager->eState>=PAGER_READER && !MEMDB ); - assert( isOpen(pPager->fd) ); - - if( NEVER(!isOpen(pPager->fd)) ){ - assert( pPager->tempFile ); - memset(pPg->pData, 0, pPager->pageSize); - return SQLITE_OK; - } - - if( rc==SQLITE_OK && !isInWal ){ - i64 iOffset = (pgno-1)*(i64)pPager->pageSize; - rc = sqlite4OsRead(pPager->fd, pPg->pData, pgsz, iOffset); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - } - - if( pgno==1 ){ - if( rc ){ - /* If the read is unsuccessful, set the dbFileVers[] to something - ** that will never be a valid file version. dbFileVers[] is a copy - ** of bytes 24..39 of the database. Bytes 28..31 should always be - ** zero or the size of the database in page. Bytes 32..35 and 35..39 - ** should be page numbers which are never 0xffffffff. So filling - ** pPager->dbFileVers[] with all 0xff bytes should suffice. - ** - ** For an encrypted database, the situation is more complex: bytes - ** 24..39 of the database are white noise. But the probability of - ** white noising equaling 16 bytes of 0xff is vanishingly small so - ** we should still be ok. - */ - memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); - }else{ - u8 *dbFileVers = &((u8*)pPg->pData)[24]; - memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); - } - } - CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM); - - PAGER_INCR(sqlite4_pager_readdb_count); - PAGER_INCR(pPager->nRead); - IOTRACE(("PGIN %p %d\n", pPager, pgno)); - PAGERTRACE(("FETCH %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pPg))); - - 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 = sqlite4Get4byte((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 called as part of the transition from PAGER_OPEN -** to PAGER_READER state to determine the size of the database file -** in pages (assuming the page size currently stored in Pager.pageSize). -** -** If no error occurs, SQLITE_OK is returned and the size of the database -** in pages is stored in *pnPage. Otherwise, an error code (perhaps -** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified. -*/ -static int pagerPagecount(Pager *pPager, Pgno *pnPage){ - Pgno nPage = 0; /* Value to return via *pnPage */ - - /* If the database size was not available from the WAL sub-system, - ** determine it based on the size of the database file. If the size - ** of the database file is not an integer multiple of the page-size, - ** round down to the nearest page. Except, any file larger than 0 - ** bytes in size is considered to contain at least one page. - */ - if( nPage==0 ){ - i64 n = 0; /* Size of db file in bytes */ - assert( isOpen(pPager->fd) || pPager->tempFile ); - if( isOpen(pPager->fd) ){ - int rc = sqlite4OsFileSize(pPager->fd, &n); - if( rc!=SQLITE_OK ){ - return rc; - } - } - nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); - } - - /* If the current number of pages in the file is greater than the - ** configured maximum pager number, increase the allowed limit so - ** that the file can be read. - */ - if( nPage>pPager->mxPgno ){ - pPager->mxPgno = (Pgno)nPage; - } - - *pnPage = nPage; - return SQLITE_OK; -} - - -/* -** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback -** the entire master journal file. The case pSavepoint==NULL occurs when -** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction -** savepoint. -** -** When pSavepoint is not NULL (meaning a non-transaction savepoint is -** being rolled back), then the rollback consists of up to three stages, -** performed in the order specified: -** -** * Pages are played back from the main journal starting at byte -** offset PagerSavepoint.iOffset and continuing to -** PagerSavepoint.iHdrOffset, or to the end of the main journal -** file if PagerSavepoint.iHdrOffset is zero. -** -** * If PagerSavepoint.iHdrOffset is not zero, then pages are played -** back starting from the journal header immediately following -** PagerSavepoint.iHdrOffset to the end of the main journal file. -** -** * Pages are then played back from the sub-journal file, starting -** with the PagerSavepoint.iSubRec and continuing to the end of -** the journal file. -** -** Throughout the rollback process, each time a page is rolled back, the -** corresponding bit is set in a bitvec structure (variable pDone in the -** implementation below). This is used to ensure that a page is only -** rolled back the first time it is encountered in either journal. -** -** If pSavepoint is NULL, then pages are only played back from the main -** journal file. There is no need for a bitvec in this case. -** -** In either case, before playback commences the Pager.dbSize variable -** is reset to the value that it held at the start of the savepoint -** (or transaction). No page with a page-number greater than this value -** is played back. If one is encountered it is simply skipped. -*/ -static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ - i64 szJ; /* Effective size of the main journal */ - i64 iHdrOff; /* End of first segment of main-journal records */ - int rc = SQLITE_OK; /* Return code */ - Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ - - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - - /* Allocate a bitvec to use to store the set of pages rolled back */ - if( pSavepoint ){ - pDone = sqlite4BitvecCreate(pSavepoint->nOrig); - if( !pDone ){ - return SQLITE_NOMEM; - } - } - - /* Set the database size back to the value it was before the savepoint - ** being reverted was opened. - */ - pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; - pPager->changeCountDone = pPager->tempFile; - - if( !pSavepoint && pagerUseWal(pPager) ){ - return pagerRollbackWal(pPager); - } - - /* Use pPager->journalOff as the effective size of the main rollback - ** journal. The actual file might be larger than this in - ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything - ** past pPager->journalOff is off-limits to us. - */ - szJ = pPager->journalOff; - assert( pagerUseWal(pPager)==0 || szJ==0 ); - - /* Begin by rolling back records from the main journal starting at - ** PagerSavepoint.iOffset and continuing to the next journal header. - ** There might be records in the main journal that have a page number - ** greater than the current database size (pPager->dbSize) but those - ** will be skipped automatically. Pages are added to pDone as they - ** are played back. - */ - if( pSavepoint && !pagerUseWal(pPager) ){ - iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ; - pPager->journalOff = pSavepoint->iOffset; - while( rc==SQLITE_OK && pPager->journalOffjournalOff, pDone, 1, 1); - } - assert( rc!=SQLITE_DONE ); - }else{ - pPager->journalOff = 0; - } - - /* Continue rolling back records out of the main journal starting at - ** the first journal header seen and continuing until the effective end - ** of the main journal file. Continue to skip out-of-range pages and - ** continue adding pages rolled back to pDone. - */ - while( rc==SQLITE_OK && pPager->journalOffjournalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" - ** test is related to ticket #2565. See the discussion in the - ** pager_playback() function for additional information. - */ - if( nJRec==0 - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff - ){ - nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); - } - for(ii=0; rc==SQLITE_OK && iijournalOffjournalOff, pDone, 1, 1); - } - assert( rc!=SQLITE_DONE ); - } - assert( rc!=SQLITE_OK || pPager->journalOff>=szJ ); - - /* Finally, rollback pages from the sub-journal. Page that were - ** previously rolled back out of the main journal (and are hence in pDone) - ** will be skipped. Out-of-range pages are also skipped. - */ - if( pSavepoint ){ - u32 ii; /* Loop counter */ - i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); - - for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && iinSubRec; ii++){ - assert( offset==(i64)ii*(4+pPager->pageSize) ); - rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); - } - assert( rc!=SQLITE_DONE ); - } - - sqlite4BitvecDestroy(pDone); - if( rc==SQLITE_OK ){ - pPager->journalOff = szJ; - } - - return rc; -} - -/* -** Change the maximum number of in-memory pages that are allowed. -*/ -void sqlite4PagerSetCachesize(Pager *pPager, int mxPage){ - sqlite4PcacheSetCachesize(pPager->pPCache, mxPage); -} - -/* -** Free as much memory as possible from the pager. -*/ -void sqlite4PagerShrink(Pager *pPager){ - sqlite4PcacheShrink(pPager->pPCache); -} - -/* -** Adjust the robustness of the database to damage due to OS crashes -** or power failures by changing the number of syncs()s when writing -** the rollback journal. There are three levels: -** -** OFF sqlite4OsSync() is never called. This is the default -** for temporary and transient files. -** -** NORMAL The journal is synced once before writes begin on the -** database. This is normally adequate protection, but -** it is theoretically possible, though very unlikely, -** that an inopertune power failure could leave the journal -** in a state which would cause damage to the database -** when it is rolled back. -** -** FULL The journal is synced twice before writes begin on the -** database (with some additional information - the nRec field -** of the journal header - being written in between the two -** syncs). If we assume that writing a -** single disk sector is atomic, then this mode provides -** 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 sqlite4PagerSetSafetyLevel( - 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; - 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 - -/* -** The following global variable is incremented whenever the library -** attempts to open a temporary file. This information is used for -** testing and analysis only. -*/ -#ifdef SQLITE_TEST -int sqlite4_opentemp_count = 0; -#endif - -/* -** Open a temporary file. -** -** Write the file descriptor into *pFile. Return SQLITE_OK on success -** or some other error code if we fail. The OS will automatically -** delete the temporary file when it is closed. -** -** The flags passed to the VFS layer xOpen() call are those specified -** by parameter vfsFlags ORed with the following: -** -** SQLITE_OPEN_READWRITE -** SQLITE_OPEN_CREATE -** SQLITE_OPEN_EXCLUSIVE -** SQLITE_OPEN_DELETEONCLOSE -*/ -static int pagerOpentemp( - Pager *pPager, /* The pager object */ - sqlite4_file *pFile, /* Write the file descriptor here */ - int vfsFlags /* Flags passed through to the VFS */ -){ - int rc; /* Return code */ - -#ifdef SQLITE_TEST - sqlite4_opentemp_count++; /* Used for testing and analysis only */ -#endif - - vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; - rc = sqlite4OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); - assert( rc!=SQLITE_OK || isOpen(pFile) ); - return rc; -} - -/* -** Set the busy handler function. -** -** The pager invokes the busy-handler if sqlite4OsLock() returns -** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock, -** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE -** lock. It does *not* invoke the busy handler when upgrading from -** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE -** (which occurs during hot-journal rollback). Summary: -** -** Transition | Invokes xBusyHandler -** -------------------------------------------------------- -** NO_LOCK -> SHARED_LOCK | Yes -** SHARED_LOCK -> RESERVED_LOCK | No -** SHARED_LOCK -> EXCLUSIVE_LOCK | No -** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes -** -** If the busy-handler callback returns non-zero, the lock is -** retried. If it returns zero, then the SQLITE_BUSY error is -** returned to the caller of the pager API function. -*/ -void sqlite4PagerSetBusyhandler( - Pager *pPager, /* Pager object */ - int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ - void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ -){ - pPager->xBusyHandler = xBusyHandler; - pPager->pBusyHandlerArg = pBusyHandlerArg; -} - -/* -** Change the page size used by the Pager object. The new page size -** is passed in *pPageSize. -** -** If the pager is in the error state when this function is called, it -** is a no-op. The value returned is the error state error code (i.e. -** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). -** -** Otherwise, if all of the following are true: -** -** * the new page size (value of *pPageSize) is valid (a power -** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and -** -** * there are no outstanding page references, and -** -** * the database is either not an in-memory database or it is -** an in-memory database that currently consists of zero pages. -** -** then the pager object page size is set to *pPageSize. -** -** If the page size is changed, then this function uses sqlite4PagerMalloc() -** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt -** fails, SQLITE_NOMEM is returned and the page size remains unchanged. -** In all other cases, SQLITE_OK is returned. -** -** If the page size is not changed, either because one of the enumerated -** conditions above is not true, the pager was in error state when this -** function was called, or because the memory allocation attempt failed, -** then *pPageSize is set to the old, retained page size before returning. -*/ -int sqlite4PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ - int rc = SQLITE_OK; - - /* It is not possible to do a full assert_pager_state() here, as this - ** function may be called from within PagerOpen(), before the state - ** of the Pager object is internally consistent. - ** - ** At one point this function returned an error if the pager was in - ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that - ** there is at least one outstanding page reference, this function - ** is a no-op for that case anyhow. - */ - - u32 pageSize = *pPageSize; - assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( (pPager->memDb==0 || pPager->dbSize==0) - && sqlite4PcacheRefCount(pPager->pPCache)==0 - && pageSize && pageSize!=(u32)pPager->pageSize - ){ - char *pNew = NULL; /* New temp space */ - i64 nByte = 0; - - if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ - rc = sqlite4OsFileSize(pPager->fd, &nByte); - } - if( rc==SQLITE_OK ){ - pNew = (char *)sqlite4PageMalloc(pageSize); - if( !pNew ) rc = SQLITE_NOMEM; - } - - if( rc==SQLITE_OK ){ - pager_reset(pPager); - pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); - pPager->pageSize = pageSize; - sqlite4PageFree(pPager->pTmpSpace); - pPager->pTmpSpace = pNew; - sqlite4PcacheSetPageSize(pPager->pPCache, pageSize); - } - } - - *pPageSize = pPager->pageSize; - if( rc==SQLITE_OK ){ - if( nReserve<0 ) nReserve = pPager->nReserve; - assert( nReserve>=0 && nReserve<1000 ); - pPager->nReserve = (i16)nReserve; - pagerReportSize(pPager); - } - return rc; -} - -/* -** Return a pointer to the "temporary page" buffer held internally -** by the pager. This is a buffer that is big enough to hold the -** entire content of a database page. This buffer is used internally -** during rollback and will be overwritten whenever a rollback -** occurs. But other modules are free to use it too, as long as -** no rollbacks are happening. -*/ -void *sqlite4PagerTempSpace(Pager *pPager){ - return pPager->pTmpSpace; -} - -/* -** Attempt to set the maximum database page count if mxPage is positive. -** Make no changes if mxPage is zero or negative. And never reduce the -** maximum page count below the current size of the database. -** -** Regardless of mxPage, return the current maximum page count. -*/ -int sqlite4PagerMaxPageCount(Pager *pPager, int mxPage){ - if( mxPage>0 ){ - pPager->mxPgno = mxPage; - } - assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ - assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */ - return pPager->mxPgno; -} - -/* -** The following set of routines are used to disable the simulated -** I/O error mechanism. These routines are used to avoid simulated -** errors in places where we do not care about errors. -** -** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops -** and generate no code. -*/ -#ifdef SQLITE_TEST -extern int sqlite4_io_error_pending; -extern int sqlite4_io_error_hit; -static int saved_cnt; -void disable_simulated_io_errors(void){ - saved_cnt = sqlite4_io_error_pending; - sqlite4_io_error_pending = -1; -} -void enable_simulated_io_errors(void){ - sqlite4_io_error_pending = saved_cnt; -} -#else -# define disable_simulated_io_errors() -# define enable_simulated_io_errors() -#endif - -/* -** Read the first N bytes from the beginning of the file into memory -** that pDest points to. -** -** If the pager was opened on a transient file (zFilename==""), or -** opened on a file less than N bytes in size, the output buffer is -** zeroed and SQLITE_OK returned. The rationale for this is that this -** function is used to read database headers, and a new transient or -** zero sized database has a header than consists entirely of zeroes. -** -** If any IO error apart from SQLITE_IOERR_SHORT_READ is encountered, -** the error code is returned to the caller and the contents of the -** output buffer undefined. -*/ -int sqlite4PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ - int rc = SQLITE_OK; - memset(pDest, 0, N); - assert( isOpen(pPager->fd) || pPager->tempFile ); - - /* This routine is only called by btree immediately after creating - ** the Pager object. There has not been an opportunity to transition - ** to WAL mode yet. - */ - assert( !pagerUseWal(pPager) ); - - if( isOpen(pPager->fd) ){ - IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) - rc = sqlite4OsRead(pPager->fd, pDest, N, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - } - return rc; -} - -/* -** This function may only be called when a read-transaction is open on -** the pager. It returns the total number of pages in the database. -** -** However, if the file is between 1 and bytes in size, then -** this is considered a 1 page file. -*/ -void sqlite4PagerPagecount(Pager *pPager, int *pnPage){ - assert( pPager->eState>=PAGER_READER ); - assert( pPager->eState!=PAGER_WRITER_FINISHED ); - *pnPage = (int)pPager->dbSize; -} - - -/* -** Try to obtain a lock of type locktype on the database file. If -** a similar or greater lock is already held, this function is a no-op -** (returning SQLITE_OK immediately). -** -** Otherwise, attempt to obtain the lock using sqlite4OsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to -** obtain the lock succeeds. -** -** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state -** variable to locktype before returning. -*/ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; /* Return code */ - - /* Check that this is either a no-op (because the requested lock is - ** already held, or one of the transistions that the busy-handler - ** may be invoked during, according to the comment above - ** sqlite4PagerSetBusyhandler(). - */ - assert( (pPager->eLock>=locktype) - || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK) - || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK) - ); - - do { - rc = pagerLockDb(pPager, locktype); - }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); - return rc; -} - -/* -** Function assertTruncateConstraint(pPager) checks that one of the -** following is true for all dirty pages currently in the page-cache: -** -** a) The page number is less than or equal to the size of the -** current database image, in pages, OR -** -** b) if the page content were written at this time, it would not -** be necessary to write the current content out to the sub-journal -** (as determined by function subjRequiresPage()). -** -** If the condition asserted by this function were not true, and the -** dirty page were to be discarded from the cache via the pagerStress() -** routine, pagerStress() would not write the current page content to -** the database file. If a savepoint transaction were rolled back after -** this happened, the correct behaviour would be to restore the current -** content of the page. However, since this content is not present in either -** the database file or the portion of the rollback journal and -** sub-journal rolled back the content could not be restored and the -** database image would become corrupt. It is therefore fortunate that -** this circumstance cannot arise. -*/ -#if defined(SQLITE_DEBUG) -static void assertTruncateConstraintCb(PgHdr *pPg){ - assert( pPg->flags&PGHDR_DIRTY ); - assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); -} -static void assertTruncateConstraint(Pager *pPager){ - sqlite4PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); -} -#else -# define assertTruncateConstraint(pPager) -#endif - -/* -** Truncate the in-memory database file image to nPage pages. This -** function does not actually modify the database file on disk. It -** just sets the internal state of the pager object so that the -** truncation will be done when the current transaction is committed. -*/ -void sqlite4PagerTruncateImage(Pager *pPager, Pgno nPage){ - assert( pPager->dbSize>=nPage ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - pPager->dbSize = nPage; - assertTruncateConstraint(pPager); -} - - -/* -** This function is called before attempting a hot-journal rollback. It -** syncs the journal file to disk, then sets pPager->journalHdr to the -** size of the journal file so that the pager_playback() routine knows -** that the entire journal file has been synced. -** -** Syncing a hot-journal to disk before attempting to roll it back ensures -** that if a power-failure occurs during the rollback, the process that -** attempts rollback following system recovery sees the same journal -** content as this process. -** -** If everything goes as planned, SQLITE_OK is returned. Otherwise, -** an SQLite error code. -*/ -static int pagerSyncHotJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !pPager->noSync ){ - rc = sqlite4OsSync(pPager->jfd, SQLITE_SYNC_NORMAL); - } - if( rc==SQLITE_OK ){ - rc = sqlite4OsFileSize(pPager->jfd, &pPager->journalHdr); - } - return rc; -} - -/* -** Shutdown the page cache. Free all memory and close all files. -** -** If a transaction was in progress when this routine is called, that -** transaction is rolled back. All outstanding pages are invalidated -** and their memory is freed. Any attempt to use a page associated -** with this page cache after this function returns will likely -** result in a coredump. -** -** This function always succeeds. If a transaction is active an attempt -** is made to roll it back. If an error occurs during the rollback -** a hot journal may be left in the filesystem but no error is returned -** to the caller. -*/ -int sqlite4PagerClose(Pager *pPager){ - u8 *pTmp = (u8 *)pPager->pTmpSpace; - - assert( assert_pager_state(pPager) ); - disable_simulated_io_errors(); - sqlite4BeginBenignMalloc(); - /* pPager->errCode = 0; */ - pPager->exclusiveMode = 0; - pager_reset(pPager); - if( MEMDB ){ - pager_unlock(pPager); - }else{ - /* If it is open, sync the journal file before calling UnlockAndRollback. - ** If this is not done, then an unsynced portion of the open journal - ** file may be played back into the database. If a power failure occurs - ** while this is happening, the database could become corrupt. - ** - ** If an error occurs while trying to sync the journal, shift the pager - ** into the ERROR state. This causes UnlockAndRollback to unlock the - ** database and close the journal file without attempting to roll it - ** back or finalize it. The next database user will have to do hot-journal - ** rollback before accessing the database file. - */ - if( isOpen(pPager->jfd) ){ - pager_error(pPager, pagerSyncHotJournal(pPager)); - } - pagerUnlockAndRollback(pPager); - } - sqlite4EndBenignMalloc(); - enable_simulated_io_errors(); - PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); - IOTRACE(("CLOSE %p\n", pPager)) - sqlite4OsClose(pPager->jfd); - sqlite4OsClose(pPager->fd); - sqlite4PageFree(pTmp); - sqlite4PcacheClose(pPager->pPCache); - -#ifdef SQLITE_HAS_CODEC - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); -#endif - - assert( !pPager->aSavepoint && !pPager->pInJournal ); - assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); - - sqlite4_free(pPager); - return SQLITE_OK; -} - -#if !defined(NDEBUG) || defined(SQLITE_TEST) -/* -** Return the page number for page pPg. -*/ -Pgno sqlite4PagerPagenumber(DbPage *pPg){ - return pPg->pgno; -} -#endif - -/* -** Increment the reference count for page pPg. -*/ -void sqlite4PagerRef(DbPage *pPg){ - sqlite4PcacheRef(pPg); -} - -/* -** Sync the journal. In other words, make sure all the pages that have -** been written to the journal have actually reached the surface of the -** disk and can be restored in the event of a hot-journal rollback. -** -** If the Pager.noSync flag is set, then this function is a no-op. -** Otherwise, the actions required depend on the journal-mode and the -** device characteristics of the the file-system, as follows: -** -** * If the journal file is an in-memory journal file, no action need -** be taken. -** -** * Otherwise, if the device does not support the SAFE_APPEND property, -** then the nRec field of the most recently written journal header -** is updated to contain the number of journal records that have -** been written following it. If the pager is operating in full-sync -** mode, then the journal file is synced before this field is updated. -** -** * If the device does not support the SEQUENTIAL property, then -** journal file is synced. -** -** Or, in pseudo-code: -** -** if( NOT ){ -** if( NOT SAFE_APPEND ){ -** if( ) xSync(); -** -** } -** if( NOT SEQUENTIAL ) xSync(); -** } -** -** If successful, this routine clears the PGHDR_NEED_SYNC flag of every -** page currently held in memory before returning SQLITE_OK. If an IO -** error is encountered, then the IO error code is returned to the caller. -*/ -static int syncJournal(Pager *pPager, int newHdr){ - int rc; /* Return code */ - - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - assert( !pagerUseWal(pPager) ); - - rc = sqlite4PagerExclusiveLock(pPager); - if( rc!=SQLITE_OK ) return rc; - - if( !pPager->noSync ){ - assert( !pPager->tempFile ); - if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ - const int iDc = sqlite4OsDeviceCharacteristics(pPager->fd); - assert( isOpen(pPager->jfd) ); - - if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - /* This block deals with an obscure problem. If the last connection - ** that wrote to this database was operating in persistent-journal - ** mode, then the journal file may at this point actually be larger - ** than Pager.journalOff bytes. If the next thing in the journal - ** file happens to be a journal-header (written as part of the - ** previous connection's transaction), and a crash or power-failure - ** occurs after nRec is updated but before this connection writes - ** anything else to the journal file (or commits/rolls back its - ** transaction), then SQLite may become confused when doing the - ** hot-journal rollback following recovery. It may roll back all - ** of this connections data, then proceed to rolling back the old, - ** out-of-date data that follows it. Database corruption. - ** - ** To work around this, if the journal file does appear to contain - ** a valid header following Pager.journalOff, then write a 0x00 - ** byte to the start of it to prevent it from being recognized. - ** - ** Variable iNextHdrOffset is set to the offset at which this - ** problematic header will occur, if it exists. aMagic is used - ** as a temporary buffer to inspect the first couple of bytes of - ** the potential journal header. - */ - i64 iNextHdrOffset; - u8 aMagic[8]; - u8 zHeader[sizeof(aJournalMagic)+4]; - - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); - - iNextHdrOffset = journalHdrOffset(pPager); - rc = sqlite4OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); - if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ - static const u8 zerobyte = 0; - rc = sqlite4OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); - } - if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ - return rc; - } - - /* Write the nRec value into the journal file header. If in - ** full-synchronous mode, sync the journal first. This ensures that - ** all data has really hit the disk before nRec is updated to mark - ** it as a candidate for rollback. - ** - ** This is not required if the persistent media supports the - ** SAFE_APPEND property. Because in this case it is not possible - ** for garbage data to be appended to the file, the nRec field - ** is populated with 0xFFFFFFFF when the journal header is written - ** and never needs to be updated. - */ - if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlite4OsSync(pPager->jfd, pPager->syncFlags); - if( rc!=SQLITE_OK ) return rc; - } - IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); - rc = sqlite4OsWrite( - pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr - ); - if( rc!=SQLITE_OK ) return rc; - } - if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlite4OsSync(pPager->jfd, pPager->syncFlags| - (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) - ); - if( rc!=SQLITE_OK ) return rc; - } - - pPager->journalHdr = pPager->journalOff; - if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - pPager->nRec = 0; - rc = writeJournalHdr(pPager); - if( rc!=SQLITE_OK ) return rc; - } - }else{ - pPager->journalHdr = pPager->journalOff; - } - } - - /* Unless the pager is in noSync mode, the journal file was just - ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on - ** all pages. - */ - sqlite4PcacheClearSyncFlags(pPager->pPCache); - pPager->eState = PAGER_WRITER_DBMOD; - assert( assert_pager_state(pPager) ); - return SQLITE_OK; -} - -/* -** The argument is the first in a linked list of dirty pages connected -** by the PgHdr.pDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** SQLITE_BUSY is returned and no data is written to the database file. -** -** If the pager is a temp-file pager and the actual file-system file -** is not yet open, it is created and opened before any data is -** written out. -** -** Once the lock has been upgraded and, if necessary, the file opened, -** the pages are written out to the database file in list order. Writing -** a page is skipped if it meets either of the following criteria: -** -** * The page number is greater than Pager.dbSize, or -** * The PGHDR_DONT_WRITE flag is set on the page. -** -** If writing out a page causes the database file to grow, Pager.dbFileSize -** is updated accordingly. If page 1 is written out, then the value cached -** in Pager.dbFileVers[] is updated to match the new value stored in -** the database file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot -** be obtained, SQLITE_BUSY is returned. -*/ -static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ - int rc = SQLITE_OK; /* Return code */ - - /* This function is only called for rollback pagers in WRITER_DBMOD state. */ - assert( !pagerUseWal(pPager) ); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - assert( pPager->eLock==EXCLUSIVE_LOCK ); - - /* If the file is a temp-file has not yet been opened, open it now. It - ** is not possible for rc to be other than SQLITE_OK if this branch - ** is taken, as pager_wait_on_lock() is a no-op for temp-files. - */ - if( !isOpen(pPager->fd) ){ - assert( pPager->tempFile && rc==SQLITE_OK ); - rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); - } - - /* Before the first write, give the VFS a hint of what the final - ** file size will be. - */ - assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); - if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ - sqlite4_int64 szFile = pPager->pageSize * (sqlite4_int64)pPager->dbSize; - sqlite4OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); - pPager->dbHintSize = pPager->dbSize; - } - - while( rc==SQLITE_OK && pList ){ - Pgno pgno = pList->pgno; - - /* If there are dirty pages in the page cache with page numbers greater - ** than Pager.dbSize, this means sqlite4PagerTruncateImage() was called to - ** make the file smaller (presumably by auto-vacuum code). Do not write - ** any such pages to the file. - ** - ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlite4PagerDontWrite()). - */ - if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ - i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ - 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); - - /* Write out the page data. */ - rc = sqlite4OsWrite(pPager->fd, pData, pPager->pageSize, offset); - - /* If page 1 was just written, update Pager.dbFileVers to match - ** the value now stored in the database file. If writing this - ** page caused the database file to grow, update dbFileSize. - */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); - } - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; - } - - /* Update any backup objects copying the contents of this pager. */ - sqlite4BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); - - PAGERTRACE(("STORE %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pList))); - IOTRACE(("PGOUT %p %d\n", pPager, pgno)); - PAGER_INCR(sqlite4_pager_writedb_count); - PAGER_INCR(pPager->nWrite); - }else{ - PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); - } - pager_set_pagehash(pList); - pList = pList->pDirty; - } - - return rc; -} - -/* -** Ensure that the sub-journal file is open. If it is already open, this -** function is a no-op. -** -** SQLITE_OK is returned if everything goes according to plan. An -** SQLITE_IOERR_XXX error code is returned if a call to sqlite4OsOpen() -** fails. -*/ -static int openSubJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !isOpen(pPager->sjfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ - sqlite4MemJournalOpen(pPager->sjfd); - }else{ - rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); - } - } - return rc; -} - -/* -** Append a record of the current state of page pPg to the sub-journal. -** It is the callers responsibility to use subjRequiresPage() to check -** that it is really required before calling this function. -** -** If successful, set the bit corresponding to pPg->pgno in the bitvecs -** for all open savepoints before returning. -** -** This function returns SQLITE_OK if everything is successful, an IO -** error code if the attempt to write to the sub-journal fails, or -** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint -** bitvec. -*/ -static int subjournalPage(PgHdr *pPg){ - int rc = SQLITE_OK; - Pager *pPager = pPg->pPager; - if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - - /* Open the sub-journal, if it has not already been opened */ - assert( pPager->useJournal ); - assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); - assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); - assert( pagerUseWal(pPager) - || pageInJournal(pPg) - || pPg->pgno>pPager->dbOrigSize - ); - rc = openSubJournal(pPager); - - /* If the sub-journal was opened successfully (or was already open), - ** write the journal record into the file. */ - if( rc==SQLITE_OK ){ - void *pData = pPg->pData; - i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); - char *pData2; - - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); - PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); - rc = write32bits(pPager->sjfd, offset, pPg->pgno); - if( rc==SQLITE_OK ){ - rc = sqlite4OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); - } - } - } - if( rc==SQLITE_OK ){ - pPager->nSubRec++; - assert( pPager->nSavepoint>0 ); - rc = addToSavepointBitvecs(pPager, pPg->pgno); - } - return rc; -} - -/* -** This function is called by the pcache layer when it has reached some -** soft memory limit. The first argument is a pointer to a Pager object -** (cast as a void*). The pager is always 'purgeable' (not an in-memory -** database). The second argument is a reference to a page that is -** currently dirty but has no outstanding references. The page -** is always associated with the Pager object passed as the first -** argument. -** -** The job of this function is to make pPg clean by writing its contents -** out to the database file, if possible. This may involve syncing the -** journal file. -** -** If successful, sqlite4PcacheMakeClean() is called on the page and -** SQLITE_OK returned. If an IO error occurs while trying to make the -** page clean, the IO error code is returned. If the page cannot be -** made clean for some other reason, but no error occurs, then SQLITE_OK -** is returned by sqlite4PcacheMakeClean() is not called. -*/ -static int pagerStress(void *p, PgHdr *pPg){ - Pager *pPager = (Pager *)p; - int rc = SQLITE_OK; - - assert( pPg->pPager==pPager ); - assert( pPg->flags&PGHDR_DIRTY ); - - /* The doNotSyncSpill flag is set during times when doing a sync of - ** journal (and adding a new header) is not allowed. This occurs - ** during calls to sqlite4PagerWrite() while trying to journal multiple - ** pages belonging to the same sector. - ** - ** The doNotSpill flag inhibits all cache spilling regardless of whether - ** or not a sync is required. This is set during a rollback. - ** - ** Spilling is also prohibited when in an error state since that could - ** lead to database corruption. In the current implementaton it - ** is impossible for sqlite4PcacheFetch() to be called with createFlag==1 - ** while in the error state, hence it is impossible for this routine to - ** be called in the error state. Nevertheless, we include a NEVER() - ** test for the error state as a safeguard against future changes. - */ - if( NEVER(pPager->errCode) ) return SQLITE_OK; - if( pPager->doNotSpill ) return SQLITE_OK; - if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){ - return SQLITE_OK; - } - - pPg->pDirty = 0; - if( pagerUseWal(pPager) ){ - /* Write a single frame for this page to the log. */ - if( subjRequiresPage(pPg) ){ - rc = subjournalPage(pPg); - } - if( rc==SQLITE_OK ){ - rc = pagerWalFrames(pPager, pPg, 0, 0); - } - }else{ - - /* Sync the journal file if required. */ - if( pPg->flags&PGHDR_NEED_SYNC - || pPager->eState==PAGER_WRITER_CACHEMOD - ){ - rc = syncJournal(pPager, 1); - } - - /* If the page number of this page is larger than the current size of - ** the database image, it may need to be written to the sub-journal. - ** This is because the call to pager_write_pagelist() below will not - ** actually write data to the file in this case. - ** - ** Consider the following sequence of events: - ** - ** BEGIN; - ** - ** - ** SAVEPOINT sp; - ** - ** pagerStress(page X) - ** ROLLBACK TO sp; - ** - ** If (X>Y), then when pagerStress is called page X will not be written - ** out to the database file, but will be dropped from the cache. Then, - ** following the "ROLLBACK TO sp" statement, reading page X will read - ** data from the database file. This will be the copy of page X as it - ** was when the transaction started, not as it was when "SAVEPOINT sp" - ** was executed. - ** - ** The solution is to write the current data for page X into the - ** sub-journal file now (if it is not already there), so that it will - ** be restored to its current value when the "ROLLBACK TO sp" is - ** executed. - */ - if( NEVER( - rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) - ) ){ - rc = subjournalPage(pPg); - } - - /* Write the contents of the page out to the database file. */ - if( rc==SQLITE_OK ){ - assert( (pPg->flags&PGHDR_NEED_SYNC)==0 ); - rc = pager_write_pagelist(pPager, pPg); - } - } - - /* Mark the page as clean. */ - if( rc==SQLITE_OK ){ - PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); - sqlite4PcacheMakeClean(pPg); - } - - return pager_error(pPager, rc); -} - - -/* -** Allocate and initialize a new Pager object and put a pointer to it -** in *ppPager. The pager should eventually be freed by passing it -** to sqlite4PagerClose(). -** -** The zFilename argument is the path to the database file to open. -** If zFilename is NULL then a randomly-named temporary file is created -** and used as the file to be cached. Temporary files are be deleted -** automatically when they are closed. If zFilename is ":memory:" then -** all information is held in cache. It is never written to disk. -** This can be used to implement an in-memory database. -** -** The nExtra parameter specifies the number of bytes of space allocated -** along with each page reference. This space is available to the user -** via the sqlite4PagerGetExtra() API. -** -** The flags argument is used to specify properties that affect the -** operation of the pager. It should be passed some bitwise combination -** of the PAGER_* flags. -** -** The vfsFlags parameter is a bitmask to pass to the flags parameter -** of the xOpen() method of the supplied VFS when opening files. -** -** If the pager object is allocated and the specified file opened -** successfully, SQLITE_OK is returned and *ppPager set to point to -** the new pager object. If an error occurs, *ppPager is set to NULL -** and error code returned. This function may return SQLITE_NOMEM -** (sqlite4Malloc() is used to allocate memory), SQLITE_CANTOPEN or -** various SQLITE_IO_XXX errors. -*/ -int sqlite4PagerOpen( - sqlite4_vfs *pVfs, /* The virtual file system to use */ - Pager **ppPager, /* OUT: Return the Pager structure here */ - const char *zFilename, /* Name of the database file to open */ - int nExtra, /* Extra bytes append to each in-memory page */ - int flags, /* flags controlling this file */ - int vfsFlags, /* flags passed through to sqlite4_vfs.xOpen() */ - void (*xReinit)(DbPage*) /* Function to reinitialize pages */ -){ - u8 *pPtr; - Pager *pPager = 0; /* Pager object to allocate and return */ - int rc = SQLITE_OK; /* Return code */ - int tempFile = 0; /* True for temp files (incl. in-memory files) */ - int memDb = 0; /* True if this is an in-memory file */ - int readOnly = 0; /* True if this is a read-only file */ - int journalFileSize; /* Bytes to allocate for each journal fd */ - char *zPathname = 0; /* Full path to database file */ - int nPathname = 0; /* Number of bytes in zPathname */ - int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ - int pcacheSize = sqlite4PcacheSize(); /* Bytes to allocate for PCache */ - u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ - const char *zUri = 0; /* URI args to copy */ - int nUri = 0; /* Number of bytes of URI args at *zUri */ - - /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). This - ** is the maximum space required for an in-memory journal file handle - ** and a regular journal file-handle. Note that a "regular journal-handle" - ** may be a wrapper capable of caching the first portion of the journal - ** file in memory to implement the atomic-write optimization (see - ** source file journal.c). - */ - if( sqlite4JournalSize(pVfs)>sqlite4MemJournalSize() ){ - journalFileSize = ROUND8(sqlite4JournalSize(pVfs)); - }else{ - journalFileSize = ROUND8(sqlite4MemJournalSize()); - } - - /* 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. - */ - if( zFilename && zFilename[0] ){ - const char *z; - nPathname = pVfs->mxPathname+1; - zPathname = sqlite4Malloc(nPathname*2); - if( zPathname==0 ){ - return SQLITE_NOMEM; - } - zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ - rc = sqlite4OsFullPathname(pVfs, zFilename, nPathname, zPathname); - nPathname = sqlite4Strlen30(zPathname); - z = zUri = &zFilename[sqlite4Strlen30(zFilename)+1]; - while( *z ){ - z += sqlite4Strlen30(z)+1; - z += sqlite4Strlen30(z)+1; - } - nUri = (int)(&z[1] - zUri); - assert( nUri>=0 ); - if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ - /* This branch is taken when the journal path required by - ** the database being opened will be more than pVfs->mxPathname - ** bytes in length. This means the database cannot be opened, - ** as it will not be possible to open the journal file or even - ** check for a hot-journal before reading. - */ - rc = SQLITE_CANTOPEN_BKPT; - } - if( rc!=SQLITE_OK ){ - sqlite4_free(zPathname); - return rc; - } - } - - /* Allocate memory for the Pager structure, PCache object, the - ** three file descriptors, the database file name and the journal - ** file name. The layout in memory is as follows: - ** - ** Pager object (sizeof(Pager) bytes) - ** PCache object (sqlite4PcacheSize() bytes) - ** Database file handle (pVfs->szOsFile bytes) - ** Sub-journal file handle (journalFileSize bytes) - ** Main journal file handle (journalFileSize bytes) - ** Database file name (nPathname+1 bytes) - ** Journal file name (nPathname+8+1 bytes) - */ - pPtr = (u8 *)sqlite4MallocZero( - ROUND8(sizeof(*pPager)) + /* Pager structure */ - ROUND8(pcacheSize) + /* PCache object */ - ROUND8(pVfs->szOsFile) + /* The main db file */ - journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + nUri + /* zFilename */ - nPathname + 8 + 2 /* zJournal */ - ); - assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); - if( !pPtr ){ - sqlite4_free(zPathname); - return SQLITE_NOMEM; - } - pPager = (Pager*)(pPtr); - pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); - pPager->fd = (sqlite4_file*)(pPtr += ROUND8(pcacheSize)); - pPager->sjfd = (sqlite4_file*)(pPtr += ROUND8(pVfs->szOsFile)); - pPager->jfd = (sqlite4_file*)(pPtr += journalFileSize); - pPager->zFilename = (char*)(pPtr += journalFileSize); - assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); - - /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ - if( zPathname ){ - assert( nPathname>0 ); - pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); - memcpy(pPager->zFilename, zPathname, nPathname); - memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); - memcpy(pPager->zJournal, zPathname, nPathname); - memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+1); - sqlite4FileSuffix3(pPager->zFilename, pPager->zJournal); - sqlite4_free(zPathname); - } - pPager->pVfs = pVfs; - pPager->vfsFlags = vfsFlags; - - /* Open the pager file. - */ - if( zFilename && zFilename[0] ){ - int fout = 0; /* VFS flags returned by xOpen() */ - rc = sqlite4OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); - assert( !memDb ); - readOnly = (fout&SQLITE_OPEN_READONLY); - - /* If the file was successfully opened for read/write access, - ** choose a default page size in case we have to create the - ** database file. The default page size is the maximum of: - ** - ** + SQLITE_DEFAULT_PAGE_SIZE, - ** + The value returned by sqlite4OsSectorSize() - ** + The largest page size that can be written atomically. - */ - if( rc==SQLITE_OK && !readOnly ){ - setSectorSize(pPager); - assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); - if( szPageDfltsectorSize ){ - if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ - szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; - }else{ - szPageDflt = (u32)pPager->sectorSize; - } - } -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - { - int iDc = sqlite4OsDeviceCharacteristics(pPager->fd); - int ii; - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); - for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ - if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ - szPageDflt = ii; - } - } - } -#endif - } - }else{ - /* If a temporary file is requested, it is not opened immediately. - ** In this case we accept the default page size and delay actually - ** opening the file until the first call to OsWrite(). - ** - ** This branch is also run for an in-memory database. An in-memory - ** database is the same as a temp-file that is never written out to - ** disk and uses an in-memory rollback journal. - */ - tempFile = 1; - pPager->eState = PAGER_READER; - pPager->eLock = EXCLUSIVE_LOCK; - readOnly = (vfsFlags&SQLITE_OPEN_READONLY); - } - - /* The following call to PagerSetPagesize() serves to set the value of - ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. - */ - if( rc==SQLITE_OK ){ - assert( pPager->memDb==0 ); - rc = sqlite4PagerSetPagesize(pPager, &szPageDflt, -1); - testcase( rc!=SQLITE_OK ); - } - - /* If an error occurred in either of the blocks above, free the - ** Pager structure and close the file. - */ - if( rc!=SQLITE_OK ){ - assert( !pPager->pTmpSpace ); - sqlite4OsClose(pPager->fd); - sqlite4_free(pPager); - return rc; - } - - /* Initialize the PCache object. */ - assert( nExtra<1000 ); - nExtra = ROUND8(nExtra); - sqlite4PcacheOpen(szPageDflt, nExtra, !memDb, - !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); - - PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); - IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) - - pPager->useJournal = (u8)useJournal; - /* pPager->stmtOpen = 0; */ - /* pPager->stmtInUse = 0; */ - /* pPager->nRef = 0; */ - /* pPager->stmtSize = 0; */ - /* pPager->stmtJSize = 0; */ - /* pPager->nPage = 0; */ - pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; - /* pPager->state = PAGER_UNLOCK; */ -#if 0 - assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) ); -#endif - /* pPager->errMask = 0; */ - pPager->tempFile = (u8)tempFile; - assert( tempFile==PAGER_LOCKINGMODE_NORMAL - || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 ); - pPager->exclusiveMode = (u8)tempFile; - pPager->changeCountDone = pPager->tempFile; - pPager->memDb = (u8)memDb; - pPager->readOnly = (u8)readOnly; - assert( useJournal || pPager->tempFile ); - pPager->noSync = pPager->tempFile; - if( pPager->noSync ){ - assert( pPager->fullSync==0 ); - assert( pPager->syncFlags==0 ); - assert( pPager->ckptSyncFlags==0 ); - }else{ - pPager->fullSync = 1; - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; - } - /* pPager->pFirst = 0; */ - /* pPager->pFirstSynced = 0; */ - /* pPager->pLast = 0; */ - pPager->nExtra = (u16)nExtra; - pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; - assert( isOpen(pPager->fd) || tempFile ); - setSectorSize(pPager); - if( !useJournal ){ - pPager->journalMode = PAGER_JOURNALMODE_OFF; - }else if( memDb ){ - pPager->journalMode = PAGER_JOURNALMODE_MEMORY; - } - /* pPager->xBusyHandler = 0; */ - /* pPager->pBusyHandlerArg = 0; */ - pPager->xReiniter = xReinit; - /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ - - *ppPager = pPager; - return SQLITE_OK; -} - - - -/* -** This function is called after transitioning from PAGER_UNLOCK to -** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that -** needs to be played back. According to this function, a hot-journal -** file exists if the following criteria are met: -** -** * The journal file exists in the file system, and -** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size, and -** * The first byte of the journal file exists and is not 0x00. -** -** If the current size of the database file is 0 but a journal file -** exists, that is probably an old journal left over from a prior -** database with the same name. In this case the journal file is -** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK -** is returned. -** -** This routine does not check if there is a master journal filename -** at the end of the file. If there is, and that master journal file -** does not exist, then the journal file is not really hot. In this -** case this routine will return a false-positive. The pager_playback() -** routine will discover that the journal file is not really hot and -** will not roll it back. -** -** If a hot-journal file is found to exist, *pExists is set to 1 and -** SQLITE_OK returned. If no hot-journal file is present, *pExists is -** set to 0 and SQLITE_OK returned. If an IO error occurs while trying -** to determine whether or not a hot-journal file exists, the IO error -** code is returned and the value of *pExists is undefined. -*/ -static int hasHotJournal(Pager *pPager, int *pExists){ - sqlite4_vfs * const pVfs = pPager->pVfs; - int rc = SQLITE_OK; /* Return code */ - int exists = 1; /* True if a journal file is present */ - int jrnlOpen = !!isOpen(pPager->jfd); - - assert( pPager->useJournal ); - assert( isOpen(pPager->fd) ); - assert( pPager->eState==PAGER_OPEN ); - - assert( jrnlOpen==0 || ( sqlite4OsDeviceCharacteristics(pPager->jfd) & - SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN - )); - - *pExists = 0; - if( !jrnlOpen ){ - rc = sqlite4OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); - } - if( rc==SQLITE_OK && exists ){ - int locked = 0; /* True if some process holds a RESERVED lock */ - - /* Race condition here: Another process might have been holding the - ** the RESERVED lock and have a journal open at the sqlite4OsAccess() - ** call above, but then delete the journal and drop the lock before - ** we get to the following sqlite4OsCheckReservedLock() call. If that - ** is the case, this routine might think there is a hot journal when - ** in fact there is none. This results in a false-positive which will - ** be dealt with by the playback routine. Ticket #3883. - */ - rc = sqlite4OsCheckReservedLock(pPager->fd, &locked); - if( rc==SQLITE_OK && !locked ){ - Pgno nPage; /* Number of pages in database file */ - - /* Check the size of the database file. If it consists of 0 pages, - ** then delete the journal file. See the header comment above for - ** the reasoning here. Delete the obsolete journal file under - ** a RESERVED lock to avoid race conditions and to avoid violating - ** [H33020]. - */ - rc = pagerPagecount(pPager, &nPage); - if( rc==SQLITE_OK ){ - if( nPage==0 ){ - sqlite4BeginBenignMalloc(); - if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ - sqlite4OsDelete(pVfs, pPager->zJournal, 0); - if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); - } - sqlite4EndBenignMalloc(); - }else{ - /* The journal file exists and no other connection has a reserved - ** or greater lock on the database file. Now check that there is - ** at least one non-zero bytes at the start of the journal file. - ** If there is, then we consider this journal to be hot. If not, - ** it can be ignored. - */ - if( !jrnlOpen ){ - int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; - rc = sqlite4OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); - } - if( rc==SQLITE_OK ){ - u8 first = 0; - rc = sqlite4OsRead(pPager->jfd, (void *)&first, 1, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - if( !jrnlOpen ){ - sqlite4OsClose(pPager->jfd); - } - *pExists = (first!=0); - }else if( rc==SQLITE_CANTOPEN ){ - /* If we cannot open the rollback journal file in order to see if - ** its has a zero header, that might be due to an I/O error, or - ** it might be due to the race condition described above and in - ** ticket #3883. Either way, assume that the journal is hot. - ** This might be a false positive. But if it is, then the - ** automatic journal playback and recovery mechanism will deal - ** with it under an EXCLUSIVE lock where we do not need to - ** worry so much with race conditions. - */ - *pExists = 1; - rc = SQLITE_OK; - } - } - } - } - } - - return rc; -} - -/* -** This function is called to obtain a shared lock on the database file. -** It is illegal to call sqlite4PagerAcquire() until after this function -** has been successfully called. If a shared-lock is already held when -** this function is called, it is a no-op. -** -** The following operations are also performed by this function. -** -** 1) If the pager is currently in PAGER_OPEN state (no lock held -** on the database file), then an attempt is made to obtain a -** SHARED lock on the database file. Immediately after obtaining -** the SHARED lock, the file-system is checked for a hot-journal, -** which is played back if present. Following any hot-journal -** rollback, the contents of the cache are validated by checking -** the 'change-counter' field of the database file header and -** discarded if they are found to be invalid. -** -** 2) If the pager is running in exclusive-mode, and there are currently -** no outstanding references to any pages, and is in the error state, -** then an attempt is made to clear the error state by discarding -** the contents of the page cache and rolling back any open journal -** file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs while locking the database, checking for a hot-journal file or -** rolling back a journal file, the IO error code is returned. -*/ -int sqlite4PagerSharedLock(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - - /* This routine is only called from b-tree and only when there are no - ** outstanding pages. This implies that the pager state should either - ** be OPEN or READER. READER is only possible if the pager is or was in - ** exclusive access mode. - */ - assert( sqlite4PcacheRefCount(pPager->pPCache)==0 ); - assert( assert_pager_state(pPager) ); - assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); - if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } - - if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ - int bHotJournal = 1; /* True if there exists a hot journal-file */ - - assert( !MEMDB ); - - rc = pager_wait_on_lock(pPager, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); - goto failed; - } - - /* If a journal file exists, and there is no RESERVED lock on the - ** database file, then it either needs to be played back or deleted. - */ - if( pPager->eLock<=SHARED_LOCK ){ - rc = hasHotJournal(pPager, &bHotJournal); - } - if( rc!=SQLITE_OK ){ - goto failed; - } - if( bHotJournal ){ - /* Get an EXCLUSIVE lock on the database file. At this point it is - ** important that a RESERVED lock is not obtained on the way to the - ** EXCLUSIVE lock. If it were, another process might open the - ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the - ** hot-journal back. - ** - ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock - ** on the database file. - ** - ** Unless the pager is in locking_mode=exclusive mode, the lock is - ** downgraded to SHARED_LOCK before this function returns. - */ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - goto failed; - } - - /* If it is not already open and the file exists on disk, open the - ** journal for read/write access. Write access is required because - ** in exclusive-access mode the file descriptor will be kept open - ** and possibly used for a transaction later on. Also, write-access - ** is usually required to finalize the journal in journal_mode=persist - ** mode (and also for journal_mode=truncate on some systems). - ** - ** If the journal does not exist, it usually means that some - ** other connection managed to get in and roll it back before - ** this connection obtained the exclusive lock above. Or, it - ** may mean that the pager was in the error-state when this - ** function was called and the journal file does not exist. - */ - if( !isOpen(pPager->jfd) ){ - sqlite4_vfs * const pVfs = pPager->pVfs; - int bExists; /* True if journal file exists */ - rc = sqlite4OsAccess( - pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); - if( rc==SQLITE_OK && bExists ){ - int fout = 0; - int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; - assert( !pPager->tempFile ); - rc = sqlite4OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ - rc = SQLITE_CANTOPEN_BKPT; - sqlite4OsClose(pPager->jfd); - } - } - } - - /* Playback and delete the journal. Drop the database write - ** lock and reacquire the read lock. Purge the cache before - ** playing back the hot-journal so that we don't end up with - ** an inconsistent cache. Sync the hot journal before playing - ** it back since the process that crashed and left the hot journal - ** probably did not sync it and we are required to always sync - ** the journal before playing it back. - */ - if( isOpen(pPager->jfd) ){ - assert( rc==SQLITE_OK ); - rc = pagerSyncHotJournal(pPager); - if( rc==SQLITE_OK ){ - rc = pager_playback(pPager, 1); - pPager->eState = PAGER_OPEN; - } - }else if( !pPager->exclusiveMode ){ - pagerUnlockDb(pPager, SHARED_LOCK); - } - - if( rc!=SQLITE_OK ){ - /* This branch is taken if an error occurs while trying to open - ** or roll back a hot-journal while holding an EXCLUSIVE lock. The - ** pager_unlock() routine will be called before returning to unlock - ** the file. If the unlock attempt fails, then Pager.eLock must be - ** set to UNKNOWN_LOCK (see the comment above the #define for - ** UNKNOWN_LOCK above for an explanation). - ** - ** In order to get pager_unlock() to do this, set Pager.eState to - ** PAGER_ERROR now. This is not actually counted as a transition - ** to ERROR state in the state diagram at the top of this file, - ** since we know that the same call to pager_unlock() will very - ** shortly transition the pager object to the OPEN state. Calling - ** assert_pager_state() would fail now, as it should not be possible - ** to be in ERROR state when there are zero outstanding page - ** references. - */ - pager_error(pPager, rc); - goto failed; - } - - assert( pPager->eState==PAGER_OPEN ); - assert( (pPager->eLock==SHARED_LOCK) - || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) - ); - } - - if( !pPager->tempFile - && (pPager->pBackup || sqlite4PcachePagecount(pPager->pPCache)>0) - ){ - /* The shared-lock has just been acquired on the database file - ** and there are already pages in the cache (from a previous - ** read or write transaction). Check to see if the database - ** has been modified. If the database has changed, flush the - ** cache. - ** - ** Database changes is detected by looking at 15 bytes beginning - ** at offset 24 into the file. The first 4 of these 16 bytes are - ** a 32-bit counter that is incremented with each change. The - ** other bytes change randomly with each file change when - ** a codec is in use. - ** - ** There is a vanishingly small chance that a change will not be - ** detected. The chance of an undetected change is so small that - ** it can be neglected. - */ - Pgno nPage = 0; - char dbFileVers[sizeof(pPager->dbFileVers)]; - - rc = pagerPagecount(pPager, &nPage); - if( rc ) goto failed; - - if( nPage>0 ){ - IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); - rc = sqlite4OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); - if( rc!=SQLITE_OK ){ - goto failed; - } - }else{ - memset(dbFileVers, 0, sizeof(dbFileVers)); - } - - if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ - pager_reset(pPager); - } - } - } - - if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ - rc = pagerPagecount(pPager, &pPager->dbSize); - } - - failed: - if( rc!=SQLITE_OK ){ - assert( !MEMDB ); - pager_unlock(pPager); - assert( pPager->eState==PAGER_OPEN ); - }else{ - pPager->eState = PAGER_READER; - } - return rc; -} - -/* -** If the reference count has reached zero, rollback any active -** transaction and unlock the pager. -** -** Except, in locking_mode=EXCLUSIVE when there is nothing to in -** the rollback journal, the unlock is not performed and there is -** nothing to rollback, so this routine is a no-op. -*/ -static void pagerUnlockIfUnused(Pager *pPager){ - if( (sqlite4PcacheRefCount(pPager->pPCache)==0) ){ - pagerUnlockAndRollback(pPager); - } -} - -/* -** Acquire a reference to page number pgno in pager pPager (a page -** reference has type DbPage*). If the requested reference is -** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. -** -** If the requested page is already in the cache, it is returned. -** Otherwise, a new page object is allocated and populated with data -** read from the database file. In some cases, the pcache module may -** choose not to allocate a new page object and may reuse an existing -** object with no outstanding references. -** -** The extra data appended to a page is always initialized to zeros the -** first time a page is loaded into memory. If the page requested is -** already in the cache when this function is called, then the extra -** data is left as it was when the page object was last used. -** -** If the database image is smaller than the requested page or if a -** non-zero value is passed as the noContent parameter and the -** requested page is not already stored in the cache, then no -** actual disk read occurs. In this case the memory image of the -** page is initialized to all zeros. -** -** If noContent is true, it means that we do not care about the contents -** of the page. This occurs in two seperate scenarios: -** -** a) When reading a free-list leaf page from the database, and -** -** b) When a savepoint is being rolled back and we need to load -** a new page into the cache to be filled with the data read -** from the savepoint journal. -** -** If noContent is true, then the data returned is zeroed instead of -** being read from the database. Additionally, the bits corresponding -** to pgno in Pager.pInJournal (bitvec of pages already written to the -** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open -** savepoints are set. This means if the page is made writable at any -** point in the future, using a call to sqlite4PagerWrite(), its contents -** will not be journaled. This saves IO. -** -** The acquisition might fail for several reasons. In all cases, -** an appropriate error code is returned and *ppPage is set to NULL. -** -** See also sqlite4PagerLookup(). Both this routine and Lookup() attempt -** to find a page in the in-memory cache first. If the page is not already -** in memory, this routine goes to disk to read it in whereas Lookup() -** just returns 0. This routine acquires a read-lock the first time it -** has to go to disk, and could also playback an old journal if necessary. -** Since Lookup() never goes to disk, it never has to deal with locks -** or journal files. -*/ -int sqlite4PagerAcquire( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int noContent /* Do not bother reading content from disk if true */ -){ - int rc; - PgHdr *pPg; - - assert( pPager->eState>=PAGER_READER ); - assert( assert_pager_state(pPager) ); - - if( pgno==0 ){ - return SQLITE_CORRUPT_BKPT; - } - - /* If the pager is in the error state, return an error immediately. - ** Otherwise, request the page from the PCache layer. */ - if( pPager->errCode!=SQLITE_OK ){ - rc = pPager->errCode; - }else{ - rc = sqlite4PcacheFetch(pPager->pPCache, pgno, 1, ppPage); - } - - if( rc!=SQLITE_OK ){ - /* Either the call to sqlite4PcacheFetch() returned an error or the - ** pager was already in the error-state when this function was called. - ** Set pPg to 0 and jump to the exception handler. */ - pPg = 0; - goto pager_acquire_err; - } - assert( (*ppPage)->pgno==pgno ); - assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); - - if( (*ppPage)->pPager && !noContent ){ - /* In this case the pcache already contains an initialized copy of - ** the page. Return without further ado. */ - assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); - pPager->nHit++; - return SQLITE_OK; - - }else{ - /* The pager cache has created a new page. Its content needs to - ** be initialized. */ - - pPg = *ppPage; - pPg->pPager = pPager; - - /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page - ** number greater than this, or the unused locking-page, is requested. */ - if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ - rc = SQLITE_CORRUPT_BKPT; - goto pager_acquire_err; - } - - if( MEMDB || pPager->dbSizefd) ){ - if( pgno>pPager->mxPgno ){ - rc = SQLITE_FULL; - goto pager_acquire_err; - } - if( noContent ){ - /* Failure to set the bits in the InJournal bit-vectors is benign. - ** It merely means that we might do some extra work to journal a - ** page that does not need to be journaled. Nevertheless, be sure - ** to test the case where a malloc error occurs while trying to set - ** a bit in a bit vector. - */ - sqlite4BeginBenignMalloc(); - if( pgno<=pPager->dbOrigSize ){ - TESTONLY( rc = ) sqlite4BitvecSet(pPager->pInJournal, pgno); - testcase( rc==SQLITE_NOMEM ); - } - TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); - testcase( rc==SQLITE_NOMEM ); - sqlite4EndBenignMalloc(); - } - memset(pPg->pData, 0, pPager->pageSize); - IOTRACE(("ZERO %p %d\n", pPager, pgno)); - }else{ - assert( pPg->pPager==pPager ); - pPager->nMiss++; - rc = readDbPage(pPg); - if( rc!=SQLITE_OK ){ - goto pager_acquire_err; - } - } - pager_set_pagehash(pPg); - } - - return SQLITE_OK; - -pager_acquire_err: - assert( rc!=SQLITE_OK ); - if( pPg ){ - sqlite4PcacheDrop(pPg); - } - pagerUnlockIfUnused(pPager); - - *ppPage = 0; - return rc; -} - -/* -** Acquire a page if it is already in the in-memory cache. Do -** not read the page from disk. Return a pointer to the page, -** or 0 if the page is not in cache. -** -** See also sqlite4PagerGet(). The difference between this routine -** and sqlite4PagerGet() is that _get() will go to the disk and read -** in the page if the page is not already in cache. This routine -** returns NULL if the page is not in cache or if a disk I/O error -** has ever happened. -*/ -DbPage *sqlite4PagerLookup(Pager *pPager, Pgno pgno){ - PgHdr *pPg = 0; - assert( pPager!=0 ); - assert( pgno!=0 ); - assert( pPager->pPCache!=0 ); - assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR ); - sqlite4PcacheFetch(pPager->pPCache, pgno, 0, &pPg); - return pPg; -} - -/* -** Release a page reference. -** -** If the number of references to the page drop to zero, then the -** page is added to the LRU list. When all references to all pages -** are released, a rollback occurs and the lock on the database is -** removed. -*/ -void sqlite4PagerUnref(DbPage *pPg){ - if( pPg ){ - Pager *pPager = pPg->pPager; - sqlite4PcacheRelease(pPg); - pagerUnlockIfUnused(pPager); - } -} - -/* -** This function is called at the start of every write transaction. -** There must already be a RESERVED or EXCLUSIVE lock on the database -** file when this routine is called. -** -** Open the journal file for pager pPager and write a journal header -** to the start of it. If there are active savepoints, open the sub-journal -** as well. This function is only used when the journal file is being -** opened to write a rollback log for a transaction. It is not used -** when opening a hot journal file to roll it back. -** -** If the journal file is already open (as it may be in exclusive mode), -** then this function just writes a journal header to the start of the -** already open file. -** -** Whether or not the journal file is opened by this function, the -** Pager.pInJournal bitvec structure is allocated. -** -** Return SQLITE_OK if everything is successful. Otherwise, return -** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or -** an IO error code if opening or writing the journal file fails. -*/ -static int pager_open_journal(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - sqlite4_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ - - assert( pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - assert( pPager->pInJournal==0 ); - - /* If already in the error state, this function is a no-op. But on - ** the other hand, this routine is never called if we are already in - ** an error state. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - pPager->pInJournal = sqlite4BitvecCreate(pPager->dbSize); - if( pPager->pInJournal==0 ){ - return SQLITE_NOMEM; - } - - /* Open the journal file if it is not already open. */ - if( !isOpen(pPager->jfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ - sqlite4MemJournalOpen(pPager->jfd); - }else{ - const int flags = /* VFS flags to open journal file */ - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - (pPager->tempFile ? - (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): - (SQLITE_OPEN_MAIN_JOURNAL) - ); - #ifdef SQLITE_ENABLE_ATOMIC_WRITE - rc = sqlite4JournalOpen( - pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) - ); - #else - rc = sqlite4OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); - #endif - } - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - } - - - /* Write the first journal header to the journal file and open - ** the sub-journal if necessary. - */ - if( rc==SQLITE_OK ){ - /* TODO: Check if all of these are really required. */ - pPager->nRec = 0; - pPager->journalOff = 0; - pPager->setMaster = 0; - pPager->journalHdr = 0; - rc = writeJournalHdr(pPager); - } - } - - if( rc!=SQLITE_OK ){ - sqlite4BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - }else{ - assert( pPager->eState==PAGER_WRITER_LOCKED ); - pPager->eState = PAGER_WRITER_CACHEMOD; - } - - return rc; -} - -/* -** Begin a write-transaction on the specified pager object. If a -** write-transaction has already been opened, this function is a no-op. -** -** If the exFlag argument is false, then acquire at least a RESERVED -** lock on the database file. If exFlag is true, then acquire at least -** an EXCLUSIVE lock. If such a lock is already held, no locking -** functions need be called. -** -** If the subjInMemory argument is non-zero, then any sub-journal opened -** within this transaction will be opened as an in-memory file. This -** has no effect if the sub-journal is already opened (as it may be when -** running in exclusive mode) or if the transaction does not require a -** sub-journal. If the subjInMemory argument is zero, then any required -** sub-journal is implemented in-memory if pPager is an in-memory database, -** or using a temporary file otherwise. -*/ -int sqlite4PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ - int rc = SQLITE_OK; - - if( pPager->errCode ) return pPager->errCode; - assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; - - if( ALWAYS(pPager->eState==PAGER_READER) ){ - assert( pPager->pInJournal==0 ); - - if( pagerUseWal(pPager) ){ - /* If the pager is configured to use locking_mode=exclusive, and an - ** exclusive lock on the database is not already held, obtain it now. - */ - if( pPager->exclusiveMode ){ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - return rc; - } - } - }else{ - /* Obtain a RESERVED lock on the database file. If the exFlag parameter - ** is true, then immediately upgrade this to an EXCLUSIVE lock. The - ** busy-handler callback can be used when upgrading to the EXCLUSIVE - ** lock, but not when obtaining the RESERVED lock. - */ - rc = pagerLockDb(pPager, RESERVED_LOCK); - if( rc==SQLITE_OK && exFlag ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - } - - if( rc==SQLITE_OK ){ - /* Change to WRITER_LOCKED state. - ** - ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD - ** when it has an open transaction, but never to DBMOD or FINISHED. - ** This is because in those states the code to roll back savepoint - ** transactions may copy data from the sub-journal into the database - ** file as well as into the page cache. Which would be incorrect in - ** WAL mode. - */ - pPager->eState = PAGER_WRITER_LOCKED; - pPager->dbHintSize = pPager->dbSize; - pPager->dbFileSize = pPager->dbSize; - pPager->dbOrigSize = pPager->dbSize; - pPager->journalOff = 0; - } - - assert( rc==SQLITE_OK || pPager->eState==PAGER_READER ); - assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - } - - PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); - return rc; -} - -/* -** Mark a single data page as writeable. The page is written into the -** main journal or sub-journal as required. If the page is written into -** one of the journals, the corresponding bit is set in the -** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs -** of any open savepoints as appropriate. -*/ -static int pager_write(PgHdr *pPg){ - void *pData = pPg->pData; - Pager *pPager = pPg->pPager; - int rc = SQLITE_OK; - - /* This routine is not called unless a write-transaction has already - ** been started. The journal file may or may not be open at this point. - ** It is never called in the ERROR state. - */ - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* If an error has been previously detected, report the same error - ** again. This should not happen, but the check provides robustness. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - /* Higher-level routines never call this function if database is not - ** writable. But check anyway, just for robustness. */ - if( NEVER(pPager->readOnly) ) return SQLITE_PERM; - - 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 sqlite4PcacheMakeDirty() on the page. - ** Otherwise, if it were done after calling sqlite4PcacheMakeDirty(), 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. - */ - sqlite4PcacheMakeDirty(pPg); - if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ - assert( !pagerUseWal(pPager) ); - }else{ - - /* The transaction journal now exists and we have a RESERVED or an - ** EXCLUSIVE lock on the main database file. Write the current page to - ** the transaction journal if it is not there already. - */ - if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ - assert( pagerUseWal(pPager)==0 ); - if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){ - u32 cksum; - char *pData2; - i64 iOff = pPager->journalOff; - - /* We should never write to the journal file the page that - ** contains the database locks. The following assert verifies - ** that we do not. */ - assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); - - assert( pPager->journalHdr<=pPager->journalOff ); - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); - cksum = pager_cksum(pPager, (u8*)pData2); - - /* Even if an IO or diskfull error occurs while journalling the - ** page in the block above, set the need-sync flag for the page. - ** Otherwise, when the transaction is rolled back, the logic in - ** playback_one_page() will think that the page needs to be restored - ** in the database file. And if an IO error occurs while doing so, - ** then corruption may follow. - */ - pPg->flags |= PGHDR_NEED_SYNC; - - rc = write32bits(pPager->jfd, iOff, pPg->pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite4OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4); - if( rc!=SQLITE_OK ) return rc; - rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum); - if( rc!=SQLITE_OK ) return rc; - - IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, - pPager->journalOff, pPager->pageSize)); - PAGER_INCR(sqlite4_pager_writej_count); - PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg))); - - pPager->journalOff += 8 + pPager->pageSize; - pPager->nRec++; - assert( pPager->pInJournal!=0 ); - rc = sqlite4BitvecSet(pPager->pInJournal, pPg->pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - rc |= addToSavepointBitvecs(pPager, pPg->pgno); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_NOMEM ); - return rc; - } - }else{ - if( pPager->eState!=PAGER_WRITER_DBMOD ){ - pPg->flags |= PGHDR_NEED_SYNC; - } - PAGERTRACE(("APPEND %d page %d needSync=%d\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0))); - } - } - - /* If the statement journal is open and the page is not in it, - ** then write the current page to the statement journal. Note that - ** the statement journal format differs from the standard journal format - ** in that it omits the checksums and the header. - */ - if( subjRequiresPage(pPg) ){ - rc = subjournalPage(pPg); - } - } - - /* Update the database size and return. - */ - if( pPager->dbSizepgno ){ - pPager->dbSize = pPg->pgno; - } - return rc; -} - -/* -** Mark a data page as writeable. This routine must be called before -** making changes to a page. The caller must check the return value -** of this function and be careful not to change any page data unless -** this routine returns SQLITE_OK. -** -** The difference between this function and pager_write() is that this -** function also deals with the special case where 2 or more pages -** fit on a single disk sector. In this case all co-resident pages -** must have been written to the journal file before returning. -** -** If an error occurs, SQLITE_NOMEM or an IO error code is returned -** as appropriate. Otherwise, SQLITE_OK. -*/ -int sqlite4PagerWrite(DbPage *pDbPage){ - int rc = SQLITE_OK; - - PgHdr *pPg = pDbPage; - Pager *pPager = pPg->pPager; - Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); - - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( pPager->eState!=PAGER_ERROR ); - assert( assert_pager_state(pPager) ); - - if( nPagePerSector>1 ){ - Pgno nPageCount; /* Total number of pages in database file */ - Pgno pg1; /* First page of the sector pPg is located on. */ - int nPage = 0; /* Number of pages starting at pg1 to journal */ - int ii; /* Loop counter */ - int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ - - /* Set the doNotSyncSpill flag to 1. This is because we cannot allow - ** a journal header to be written between the pages journaled by - ** this function. - */ - assert( !MEMDB ); - assert( pPager->doNotSyncSpill==0 ); - pPager->doNotSyncSpill++; - - /* This trick assumes that both the page-size and sector-size are - ** an integer power of 2. It sets variable pg1 to the identifier - ** of the first page of the sector pPg is located on. - */ - pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; - - nPageCount = pPager->dbSize; - if( pPg->pgno>nPageCount ){ - nPage = (pPg->pgno - pg1)+1; - }else if( (pg1+nPagePerSector-1)>nPageCount ){ - nPage = nPageCount+1-pg1; - }else{ - nPage = nPagePerSector; - } - assert(nPage>0); - assert(pg1<=pPg->pgno); - assert((pg1+nPage)>pPg->pgno); - - for(ii=0; iipgno || !sqlite4BitvecTest(pPager->pInJournal, pg) ){ - if( pg!=PAGER_MJ_PGNO(pPager) ){ - rc = sqlite4PagerGet(pPager, pg, &pPage); - if( rc==SQLITE_OK ){ - rc = pager_write(pPage); - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlite4PagerUnref(pPage); - } - } - }else if( (pPage = pager_lookup(pPager, pg))!=0 ){ - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlite4PagerUnref(pPage); - } - } - - /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages - ** starting at pg1, then it needs to be set for all of them. Because - ** writing to any of these nPage pages may damage the others, the - ** journal file must contain sync()ed copies of all of them - ** before any of them can be written out to the database file. - */ - if( rc==SQLITE_OK && needSync ){ - assert( !MEMDB ); - for(ii=0; iiflags |= PGHDR_NEED_SYNC; - sqlite4PagerUnref(pPage); - } - } - } - - assert( pPager->doNotSyncSpill==1 ); - pPager->doNotSyncSpill--; - }else{ - rc = pager_write(pDbPage); - } - return rc; -} - -/* -** Return TRUE if the page given in the argument was previously passed -** to sqlite4PagerWrite(). In other words, return TRUE if it is ok -** to change the content of the page. -*/ -#ifndef NDEBUG -int sqlite4PagerIswriteable(DbPage *pPg){ - return pPg->flags&PGHDR_DIRTY; -} -#endif - -/* -** A call to this routine tells the pager that it is not necessary to -** write the information on page pPg back to the disk, even though -** that page might be marked as dirty. This happens, for example, when -** the page has been added as a leaf of the freelist and so its -** content no longer matters. -** -** The overlying software layer calls this routine when all of the data -** on the given page is unused. The pager marks the page as clean so -** that it does not get written to disk. -** -** Tests show that this optimization can quadruple the speed of large -** DELETE operations. -*/ -void sqlite4PagerDontWrite(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ - PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); - IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) - pPg->flags |= PGHDR_DONT_WRITE; - pager_set_pagehash(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. 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 -** sqlite4PagerWrite() on page 1, then modifying the contents of the -** page data. In this case the file will be updated when the current -** transaction is committed. -** -** The isDirectMode flag may only be non-zero if the library was compiled -** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, -** if isDirect is non-zero, then the database file is updated directly -** by writing an updated version of page 1 using a call to the -** sqlite4OsWrite() function. -*/ -static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ - int rc = SQLITE_OK; - - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* Declare and initialize constant integer 'isDirect'. If the - ** atomic-write optimization is enabled in this build, then isDirect - ** is initialized to the value passed as the isDirectMode parameter - ** to this function. Otherwise, it is always set to zero. - ** - ** The idea is that if the atomic-write optimization is not - ** enabled at compile time, the compiler can omit the tests of - ** 'isDirect' below, as well as the block enclosed in the - ** "if( isDirect )" condition. - */ -#ifndef SQLITE_ENABLE_ATOMIC_WRITE -# define DIRECT_MODE 0 - assert( isDirectMode==0 ); - UNUSED_PARAMETER(isDirectMode); -#else -# define DIRECT_MODE isDirectMode -#endif - - if( !pPager->changeCountDone && pPager->dbSize>0 ){ - PgHdr *pPgHdr; /* Reference to page 1 */ - - assert( !pPager->tempFile && isOpen(pPager->fd) ); - - /* Open page 1 of the file for writing. */ - rc = sqlite4PagerGet(pPager, 1, &pPgHdr); - assert( pPgHdr==0 || rc==SQLITE_OK ); - - /* If page one was fetched successfully, and this function is not - ** operating in direct-mode, make page 1 writable. When not in - ** direct mode, page 1 is always held in cache and hence the PagerGet() - ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. - */ - if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ - rc = sqlite4PagerWrite(pPgHdr); - } - - if( rc==SQLITE_OK ){ - /* 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 ){ - const void *zBuf; - assert( pPager->dbFileSize>0 ); - CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); - if( rc==SQLITE_OK ){ - rc = sqlite4OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - } - if( rc==SQLITE_OK ){ - pPager->changeCountDone = 1; - } - }else{ - pPager->changeCountDone = 1; - } - } - - /* Release the page reference. */ - sqlite4PagerUnref(pPgHdr); - } - return rc; -} - -/* -** 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 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 sqlite4PagerSync(Pager *pPager){ - int rc = SQLITE_OK; - if( !pPager->noSync ){ - assert( !MEMDB ); - rc = sqlite4OsSync(pPager->fd, pPager->syncFlags); - }else if( isOpen(pPager->fd) ){ - assert( !MEMDB ); - rc = sqlite4OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0); - if( rc==SQLITE_NOTFOUND ){ - rc = SQLITE_OK; - } - } - return rc; -} - -/* -** This function may only be called while a write-transaction is active in -** rollback. If the connection is in WAL mode, this call is a no-op. -** Otherwise, if the connection does not already have an EXCLUSIVE lock on -** the database file, an attempt is made to obtain one. -** -** If the EXCLUSIVE lock is already held or the attempt to obtain it is -** successful, or the connection is in WAL mode, SQLITE_OK is returned. -** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is -** returned. -*/ -int sqlite4PagerExclusiveLock(Pager *pPager){ - int rc = SQLITE_OK; - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_WRITER_LOCKED - ); - assert( assert_pager_state(pPager) ); - if( 0==pagerUseWal(pPager) ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - return rc; -} - -/* -** Sync the database file for the pager pPager. zMaster points to the name -** of a master journal file that should be written into the individual -** journal file. zMaster may be NULL, which is interpreted as no master -** journal (a single database transaction). -** -** This routine ensures that: -** -** * The database file change-counter is updated, -** * the journal is synced (unless the atomic-write optimization is used), -** * all dirty pages are written to the database file, -** * the database file is truncated (if required), and -** * the database file synced. -** -** The only thing that remains to commit the transaction is to finalize -** (delete, truncate or zero the first part of) the journal file (or -** delete the master journal file if specified). -** -** Note that if zMaster==NULL, this does not overwrite a previous value -** passed to an sqlite4PagerCommitPhaseOne() call. -** -** If the final parameter - noSync - is true, then the database file itself -** is not synced. The caller must call sqlite4PagerSync() directly to -** sync the database file before calling CommitPhaseTwo() to delete the -** journal file in this case. -*/ -int sqlite4PagerCommitPhaseOne( - Pager *pPager, /* Pager object */ - const char *zMaster, /* If not NULL, the master journal name */ - int noSync /* True to omit the xSync on the db file */ -){ - int rc = SQLITE_OK; /* Return code */ - - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_ERROR - ); - assert( assert_pager_state(pPager) ); - - /* If a prior error occurred, report that error again. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", - pPager->zFilename, zMaster, pPager->dbSize)); - - /* If no database changes have been made, return early. */ - if( pPager->eStatepBackup); - }else{ - if( pagerUseWal(pPager) ){ - PgHdr *pList = sqlite4PcacheDirtyList(pPager->pPCache); - PgHdr *pPageOne = 0; - if( pList==0 ){ - /* Must have at least one page for the WAL commit flag. - ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ - rc = sqlite4PagerGet(pPager, 1, &pPageOne); - pList = pPageOne; - pList->pDirty = 0; - } - assert( rc==SQLITE_OK ); - if( ALWAYS(pList) ){ - rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); - } - sqlite4PagerUnref(pPageOne); - if( rc==SQLITE_OK ){ - sqlite4PcacheCleanAll(pPager->pPCache); - } - }else{ - /* The following block updates the change-counter. Exactly how it - ** does this depends on whether or not the atomic-update optimization - ** was enabled at compile time, and if this transaction meets the - ** runtime criteria to use the operation: - ** - ** * The file-system supports the atomic-write property for - ** blocks of size page-size, and - ** * This commit is not part of a multi-file transaction, and - ** * Exactly one page has been modified and store in the journal file. - ** - ** If the optimization was not enabled at compile time, then the - ** pager_incr_changecounter() function is called to update the change - ** counter in 'indirect-mode'. If the optimization is compiled in but - ** is not applicable to this transaction, call sqlite4JournalCreate() - ** to make sure the journal file has actually been created, then call - ** pager_incr_changecounter() to update the change-counter in indirect - ** mode. - ** - ** Otherwise, if the optimization is both enabled and applicable, - ** then call pager_incr_changecounter() to update the change-counter - ** in 'direct' mode. In this case the journal file will never be - ** created for this transaction. - */ - #ifdef SQLITE_ENABLE_ATOMIC_WRITE - PgHdr *pPg; - assert( isOpen(pPager->jfd) - || pPager->journalMode==PAGER_JOURNALMODE_OFF - || pPager->journalMode==PAGER_JOURNALMODE_WAL - ); - if( !zMaster && isOpen(pPager->jfd) - && pPager->journalOff==jrnlBufferSize(pPager) - && pPager->dbSize>=pPager->dbOrigSize - && (0==(pPg = sqlite4PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) - ){ - /* Update the db file change counter via the direct-write method. The - ** following call will modify the in-memory representation of page 1 - ** to include the updated change counter and then write page 1 - ** directly to the database file. Because of the atomic-write - ** property of the host file-system, this is safe. - */ - rc = pager_incr_changecounter(pPager, 1); - }else{ - rc = sqlite4JournalCreate(pPager->jfd); - if( rc==SQLITE_OK ){ - rc = pager_incr_changecounter(pPager, 0); - } - } - #else - rc = pager_incr_changecounter(pPager, 0); - #endif - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* If this transaction has made the database smaller, then all pages - ** being discarded by the truncation must be written to the journal - ** file. This can only happen in auto-vacuum mode. - ** - ** Before reading the pages with page numbers larger than the - ** current value of Pager.dbSize, set dbSize back to the value - ** that it took at the start of the transaction. Otherwise, the - ** calls to sqlite4PagerGet() return zeroed pages instead of - ** reading data from the database file. - */ - #ifndef SQLITE_OMIT_AUTOVACUUM - if( pPager->dbSizedbOrigSize - && pPager->journalMode!=PAGER_JOURNALMODE_OFF - ){ - Pgno i; /* Iterator variable */ - const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ - const Pgno dbSize = pPager->dbSize; /* Database image size */ - pPager->dbSize = pPager->dbOrigSize; - for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){ - if( !sqlite4BitvecTest(pPager->pInJournal, i) && i!=iSkip ){ - PgHdr *pPage; /* Page to journal */ - rc = sqlite4PagerGet(pPager, i, &pPage); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - rc = sqlite4PagerWrite(pPage); - sqlite4PagerUnref(pPage); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - } - } - pPager->dbSize = dbSize; - } - #endif - - /* Write the master journal name into the journal file. If a master - ** journal file name has already been written to the journal file, - ** or if zMaster is NULL (no master journal), then this call is a no-op. - */ - rc = writeMasterJournal(pPager, zMaster); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* Sync the journal file and write all dirty pages to the database. - ** If the atomic-update optimization is being used, this sync will not - ** create the journal file or perform any real IO. - ** - ** Because the change-counter page was just modified, unless the - ** atomic-update optimization is used it is almost certain that the - ** journal requires a sync here. However, in locking_mode=exclusive - ** on a system under memory pressure it is just possible that this is - ** not the case. In this case it is likely enough that the redundant - ** xSync() call will be changed to a no-op by the OS anyhow. - */ - rc = syncJournal(pPager, 0); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - rc = pager_write_pagelist(pPager,sqlite4PcacheDirtyList(pPager->pPCache)); - if( rc!=SQLITE_OK ){ - assert( rc!=SQLITE_IOERR_BLOCKED ); - goto commit_phase_one_exit; - } - sqlite4PcacheCleanAll(pPager->pPCache); - - /* If the file on disk is not the same size as the database image, - ** then use pager_truncate to grow or shrink the file here. - */ - if( pPager->dbSize!=pPager->dbFileSize ){ - Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - rc = pager_truncate(pPager, nNew); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - } - - /* Finally, sync the database file. */ - if( !noSync ){ - rc = sqlite4PagerSync(pPager); - } - IOTRACE(("DBSYNC %p\n", pPager)) - } - } - -commit_phase_one_exit: - if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ - pPager->eState = PAGER_WRITER_FINISHED; - } - return rc; -} - - -/* -** When this function is called, the database file has been completely -** updated to reflect the changes made by the current transaction and -** synced to disk. The journal file still exists in the file-system -** though, and if a failure occurs at this point it will eventually -** be used as a hot-journal and the current transaction rolled back. -** -** This function finalizes the journal file, either by deleting, -** truncating or partially zeroing it, so that it cannot be used -** for hot-journal rollback. Once this is done the transaction is -** irrevocably committed. -** -** If an error occurs, an IO error code is returned and the pager -** moves into the error state. Otherwise, SQLITE_OK is returned. -*/ -int sqlite4PagerCommitPhaseTwo(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - - /* This routine should not be called if a prior error has occurred. - ** But if (due to a coding error elsewhere in the system) it does get - ** called, just return the same error code without doing anything. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_FINISHED - || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD) - ); - assert( assert_pager_state(pPager) ); - - /* An optimization. If the database was not actually modified during - ** this transaction, the pager is running in exclusive-mode and is - ** using persistent journals, then this function is a no-op. - ** - ** The start of the journal file currently contains a single journal - ** header with the nRec field set to 0. If such a journal is used as - ** a hot-journal during hot-journal rollback, 0 changes will be made - ** to the database file. So there is no need to zero the journal - ** header. Since the pager is in exclusive mode, there is no need - ** to drop any locks either. - */ - if( pPager->eState==PAGER_WRITER_LOCKED - && pPager->exclusiveMode - && pPager->journalMode==PAGER_JOURNALMODE_PERSIST - ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); - pPager->eState = PAGER_READER; - return SQLITE_OK; - } - - PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setMaster); - return pager_error(pPager, rc); -} - -/* -** If a write transaction is open, then all changes made within the -** transaction are reverted and the current write-transaction is closed. -** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR -** state if an error occurs. -** -** If the pager is already in PAGER_ERROR state when this function is called, -** it returns Pager.errCode immediately. No work is performed in this case. -** -** Otherwise, in rollback mode, this function performs two functions: -** -** 1) It rolls back the journal file, restoring all database file and -** in-memory cache pages to the state they were in when the transaction -** was opened, and -** -** 2) It finalizes the journal file, so that it is not used for hot -** rollback at any point in the future. -** -** Finalization of the journal file (task 2) is only performed if the -** rollback is successful. -** -** In WAL mode, all cache-entries containing data modified within the -** current transaction are either expelled from the cache or reverted to -** their pre-transaction state by re-reading data from the database or -** WAL files. The WAL transaction is then closed. -*/ -int sqlite4PagerRollback(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); - - /* PagerRollback() is a no-op if called in READER or OPEN state. If - ** the pager is already in the ERROR state, the rollback is not - ** attempted here. Instead, the error code is returned to the caller. - */ - assert( assert_pager_state(pPager) ); - if( pPager->eState==PAGER_ERROR ) return pPager->errCode; - if( pPager->eState<=PAGER_READER ) return SQLITE_OK; - - if( pagerUseWal(pPager) ){ - int rc2; - rc = sqlite4PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); - 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); - } - - assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); - assert( rc==SQLITE_OK || rc==SQLITE_FULL - || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR ); - - /* If an error occurs during a ROLLBACK, we can no longer trust the pager - ** cache. So call pager_error() on the way out to make any error persistent. - */ - return pager_error(pPager, rc); -} - -/* -** Return TRUE if the database file is opened read-only. Return FALSE -** if the database is (in theory) writable. -*/ -u8 sqlite4PagerIsreadonly(Pager *pPager){ - return pPager->readOnly; -} - -/* -** Return the number of references to the pager. -*/ -int sqlite4PagerRefcount(Pager *pPager){ - return sqlite4PcacheRefCount(pPager->pPCache); -} - -/* -** Return the approximate number of bytes of memory currently -** used by the pager and its associated cache. -*/ -int sqlite4PagerMemUsed(Pager *pPager){ - int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) - + 5*sizeof(void*); - return perPageSize*sqlite4PcachePagecount(pPager->pPCache) - + sqlite4MallocSize(pPager) - + pPager->pageSize; -} - -/* -** Return the number of references to the specified page. -*/ -int sqlite4PagerPageRefcount(DbPage *pPage){ - return sqlite4PcachePageRefcount(pPage); -} - -#ifdef SQLITE_TEST -/* -** This routine is used for testing and analysis only. -*/ -int *sqlite4PagerStats(Pager *pPager){ - static int a[11]; - a[0] = sqlite4PcacheRefCount(pPager->pPCache); - a[1] = sqlite4PcachePagecount(pPager->pPCache); - a[2] = sqlite4PcacheGetCachesize(pPager->pPCache); - a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; - a[4] = pPager->eState; - a[5] = pPager->errCode; - a[6] = pPager->nHit; - a[7] = pPager->nMiss; - a[8] = 0; /* Used to be pPager->nOvfl */ - a[9] = pPager->nRead; - a[10] = pPager->nWrite; - return a; -} -#endif - -/* -** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or -** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the -** current cache hit or miss count, according to the value of eStat. If the -** reset parameter is non-zero, the cache hit or miss count is zeroed before -** returning. -*/ -void sqlite4PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ - int *piStat; - - assert( eStat==SQLITE_DBSTATUS_CACHE_HIT - || eStat==SQLITE_DBSTATUS_CACHE_MISS - ); - if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){ - piStat = &pPager->nHit; - }else{ - piStat = &pPager->nMiss; - } - - *pnVal += *piStat; - if( reset ){ - *piStat = 0; - } -} - -/* -** Return true if this is an in-memory pager. -*/ -int sqlite4PagerIsMemdb(Pager *pPager){ - return MEMDB; -} - -/* -** Check that there are at least nSavepoint savepoints open. If there are -** currently less than nSavepoints open, then open one or more savepoints -** to make up the difference. If the number of savepoints is already -** equal to nSavepoint, then this function is a no-op. -** -** If a memory allocation fails, SQLITE_NOMEM is returned. If an error -** occurs while opening the sub-journal file, then an IO error code is -** returned. Otherwise, SQLITE_OK. -*/ -int sqlite4PagerOpenSavepoint(Pager *pPager, int nSavepoint){ - int rc = SQLITE_OK; /* Return code */ - int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ - - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - - if( nSavepoint>nCurrent && pPager->useJournal ){ - int ii; /* Iterator variable */ - PagerSavepoint *aNew; /* New Pager.aSavepoint array */ - - /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM - ** if the allocation fails. Otherwise, zero the new portion in case a - ** malloc failure occurs while populating it in the for(...) loop below. - */ - aNew = (PagerSavepoint *)sqlite4Realloc( - pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint - ); - if( !aNew ){ - return SQLITE_NOMEM; - } - memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); - pPager->aSavepoint = aNew; - - /* Populate the PagerSavepoint structures just allocated. */ - for(ii=nCurrent; iidbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ - aNew[ii].iOffset = pPager->journalOff; - }else{ - aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); - } - aNew[ii].iSubRec = pPager->nSubRec; - aNew[ii].pInSavepoint = sqlite4BitvecCreate(pPager->dbSize); - if( !aNew[ii].pInSavepoint ){ - return SQLITE_NOMEM; - } - pPager->nSavepoint = ii+1; - } - assert( pPager->nSavepoint==nSavepoint ); - assertTruncateConstraint(pPager); - } - - return rc; -} - -/* -** This function is called to rollback or release (commit) a savepoint. -** The savepoint to release or rollback need not be the most recently -** created savepoint. -** -** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. -** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with -** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes -** that have occurred since the specified savepoint was created. -** -** The savepoint to rollback or release is identified by parameter -** iSavepoint. A value of 0 means to operate on the outermost savepoint -** (the first created). A value of (Pager.nSavepoint-1) means operate -** on the most recently created savepoint. If iSavepoint is greater than -** (Pager.nSavepoint-1), then this function is a no-op. -** -** If a negative value is passed to this function, then the current -** transaction is rolled back. This is different to calling -** sqlite4PagerRollback() because this function does not terminate -** the transaction or unlock the database, it just restores the -** contents of the database to its original state. -** -** In any case, all savepoints with an index greater than iSavepoint -** are destroyed. If this is a release operation (op==SAVEPOINT_RELEASE), -** then savepoint iSavepoint is also destroyed. -** -** This function may return SQLITE_NOMEM if a memory allocation fails, -** or an IO error code if an IO error occurs while rolling back a -** savepoint. If no errors occur, SQLITE_OK is returned. -*/ -int sqlite4PagerSavepoint(Pager *pPager, int op, int iSavepoint){ - int rc = pPager->errCode; /* Return code */ - - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); - - if( rc==SQLITE_OK && iSavepointnSavepoint ){ - int ii; /* Iterator variable */ - int nNew; /* Number of remaining savepoints after this op. */ - - /* Figure out how many savepoints will still be active after this - ** operation. Store this value in nNew. Then free resources associated - ** with any savepoints that are destroyed by this operation. - */ - nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1); - for(ii=nNew; iinSavepoint; ii++){ - sqlite4BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - pPager->nSavepoint = nNew; - - /* If this is a release of the outermost savepoint, truncate - ** the sub-journal to zero bytes in size. */ - if( op==SAVEPOINT_RELEASE ){ - if( nNew==0 && isOpen(pPager->sjfd) ){ - /* Only truncate if it is an in-memory sub-journal. */ - if( sqlite4IsMemJournal(pPager->sjfd) ){ - rc = sqlite4OsTruncate(pPager->sjfd, 0); - assert( rc==SQLITE_OK ); - } - pPager->nSubRec = 0; - } - } - /* Else this is a rollback operation, playback the specified savepoint. - ** If this is a temp-file, it is possible that the journal file has - ** not yet been opened. In this case there have been no changes to - ** the database file, so the playback operation can be skipped. - */ - else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ - PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; - rc = pagerPlaybackSavepoint(pPager, pSavepoint); - assert(rc!=SQLITE_DONE); - } - } - - return rc; -} - -/* -** Return the full pathname of the database file. -*/ -const char *sqlite4PagerFilename(Pager *pPager){ - return pPager->zFilename; -} - -/* -** Return the VFS structure for the pager. -*/ -const sqlite4_vfs *sqlite4PagerVfs(Pager *pPager){ - return pPager->pVfs; -} - -/* -** Return the file handle for the database file associated -** with the pager. This might return NULL if the file has -** not yet been opened. -*/ -sqlite4_file *sqlite4PagerFile(Pager *pPager){ - return pPager->fd; -} - -/* -** Return the full pathname of the journal file. -*/ -const char *sqlite4PagerJournalname(Pager *pPager){ - return pPager->zJournal; -} - -/* -** Return true if fsync() calls are disabled for this pager. Return FALSE -** if fsync()s are executed normally. -*/ -int sqlite4PagerNosync(Pager *pPager){ - return pPager->noSync; -} - -#ifdef SQLITE_HAS_CODEC -/* -** Set or retrieve the codec for this pager -*/ -void sqlite4PagerSetCodec( - Pager *pPager, - void *(*xCodec)(void*,void*,Pgno,int), - void (*xCodecSizeChng)(void*,int,int), - void (*xCodecFree)(void*), - void *pCodec -){ - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); - pPager->xCodec = pPager->memDb ? 0 : xCodec; - pPager->xCodecSizeChng = xCodecSizeChng; - pPager->xCodecFree = xCodecFree; - pPager->pCodec = pCodec; - pagerReportSize(pPager); -} -void *sqlite4PagerGetCodec(Pager *pPager){ - return pPager->pCodec; -} -#endif - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Move the page pPg to location pgno in the file. -** -** There must be no references to the page previously located at -** pgno (which we call pPgOld) though that page is allowed to be -** in cache. If the page previously located at pgno is not already -** in the rollback journal, it is not put there by by this routine. -** -** References to the page pPg remain valid. Updating any -** meta-data associated with pPg (i.e. data stored in the nExtra bytes -** allocated along with the page) is the responsibility of the caller. -** -** A transaction must be active when this routine is called. It used to be -** required that a statement transaction was not active, but this restriction -** has been removed (CREATE INDEX needs to move a page when a statement -** transaction is active). -** -** If the fourth argument, isCommit, is non-zero, then this page is being -** moved as part of a database reorganization just before the transaction -** is being committed. In this case, it is guaranteed that the database page -** pPg refers to will not be written to again within this transaction. -** -** This function may return SQLITE_NOMEM or an IO error code if an error -** occurs. Otherwise, it returns SQLITE_OK. -*/ -int sqlite4PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ - PgHdr *pPgOld; /* The page being overwritten. */ - Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ - int rc; /* Return code */ - Pgno origPgno; /* The original page number */ - - assert( pPg->nRef>0 ); - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* In order to be able to rollback, an in-memory database must journal - ** the page we are moving from. - */ - if( MEMDB ){ - rc = sqlite4PagerWrite(pPg); - if( rc ) return rc; - } - - /* If the page being moved is dirty and has not been saved by the latest - ** savepoint, then save the current contents of the page into the - ** sub-journal now. This is required to handle the following scenario: - ** - ** BEGIN; - ** - ** SAVEPOINT one; - ** - ** ROLLBACK TO one; - ** - ** If page X were not written to the sub-journal here, it would not - ** be possible to restore its contents when the "ROLLBACK TO one" - ** statement were is processed. - ** - ** subjournalPage() may need to allocate space to store pPg->pgno into - ** one or more savepoint bitvecs. This is the reason this function - ** may return SQLITE_NOMEM. - */ - if( pPg->flags&PGHDR_DIRTY - && subjRequiresPage(pPg) - && SQLITE_OK!=(rc = subjournalPage(pPg)) - ){ - return rc; - } - - PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n", - PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno)); - IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno)) - - /* If the journal needs to be sync()ed before page pPg->pgno can - ** be written to, store pPg->pgno in local variable needSyncPgno. - ** - ** If the isCommit flag is set, there is no need to remember that - ** the journal needs to be sync()ed before database page pPg->pgno - ** can be written to. The caller has already promised not to write to it. - */ - if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ - needSyncPgno = pPg->pgno; - assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); - assert( pPg->flags&PGHDR_DIRTY ); - } - - /* If the cache contains a page with page-number pgno, remove it - ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for - ** page pgno before the 'move' operation, it needs to be retained - ** for the page moved there. - */ - pPg->flags &= ~PGHDR_NEED_SYNC; - pPgOld = pager_lookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 ); - if( pPgOld ){ - pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); - if( MEMDB ){ - /* Do not discard pages from an in-memory database since we might - ** need to rollback later. Just move the page out of the way. */ - sqlite4PcacheMove(pPgOld, pPager->dbSize+1); - }else{ - sqlite4PcacheDrop(pPgOld); - } - } - - origPgno = pPg->pgno; - sqlite4PcacheMove(pPg, pgno); - sqlite4PcacheMakeDirty(pPg); - - /* For an in-memory database, make sure the original page continues - ** to exist, in case the transaction needs to roll back. Use pPgOld - ** as the original page since it has already been allocated. - */ - if( MEMDB ){ - assert( pPgOld ); - sqlite4PcacheMove(pPgOld, origPgno); - sqlite4PagerUnref(pPgOld); - } - - if( needSyncPgno ){ - /* If needSyncPgno is non-zero, then the journal file needs to be - ** sync()ed before any data is written to database file page needSyncPgno. - ** Currently, no such page exists in the page-cache and the - ** "is journaled" bitvec flag has been set. This needs to be remedied by - ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC - ** flag. - ** - ** If the attempt to load the page into the page-cache fails, (due - ** to a malloc() or IO failure), clear the bit in the pInJournal[] - ** array. Otherwise, if the page is loaded and written again in - ** this transaction, it may be written to the database file before - ** it is synced into the journal file. This way, it may end up in - ** the journal file twice, but that is not a problem. - */ - PgHdr *pPgHdr; - rc = sqlite4PagerGet(pPager, needSyncPgno, &pPgHdr); - if( rc!=SQLITE_OK ){ - if( needSyncPgno<=pPager->dbOrigSize ){ - assert( pPager->pTmpSpace!=0 ); - sqlite4BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); - } - return rc; - } - pPgHdr->flags |= PGHDR_NEED_SYNC; - sqlite4PcacheMakeDirty(pPgHdr); - sqlite4PagerUnref(pPgHdr); - } - - return SQLITE_OK; -} -#endif - -/* -** Return a pointer to the data for the specified page. -*/ -void *sqlite4PagerGetData(DbPage *pPg){ - assert( pPg->nRef>0 || pPg->pPager->memDb ); - return pPg->pData; -} - -/* -** Return a pointer to the Pager.nExtra bytes of "extra" space -** allocated along with the specified page. -*/ -void *sqlite4PagerGetExtra(DbPage *pPg){ - return pPg->pExtra; -} - -/* -** Get/set the locking-mode for this pager. Parameter eMode must be one -** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then -** the locking-mode is set to the value specified. -** -** The returned value is either PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated) -** locking-mode. -*/ -int sqlite4PagerLockingMode(Pager *pPager, int eMode){ - assert( eMode==PAGER_LOCKINGMODE_QUERY - || eMode==PAGER_LOCKINGMODE_NORMAL - || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_QUERY<0 ); - assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); - if( eMode>=0 && !pPager->tempFile ){ - pPager->exclusiveMode = (u8)eMode; - } - return (int)pPager->exclusiveMode; -} - -/* -** Set the journal-mode for this pager. Parameter eMode must be one of: -** -** PAGER_JOURNALMODE_DELETE -** PAGER_JOURNALMODE_TRUNCATE -** PAGER_JOURNALMODE_PERSIST -** PAGER_JOURNALMODE_OFF -** PAGER_JOURNALMODE_MEMORY -** PAGER_JOURNALMODE_WAL -** -** The journalmode is set to the value specified if the change is allowed. -** The change may be disallowed for the following reasons: -** -** * An in-memory database can only have its journal_mode set to _OFF -** or _MEMORY. -** -** * Temporary databases cannot have _WAL journalmode. -** -** The returned indicate the current (possibly updated) journal-mode. -*/ -int sqlite4PagerSetJournalMode(Pager *pPager, int eMode){ - u8 eOld = pPager->journalMode; /* Prior journalmode */ - -#ifdef SQLITE_DEBUG - /* The print_pager_state() routine is intended to be used by the debugger - ** only. We invoke it once here to suppress a compiler warning. */ - print_pager_state(pPager); -#endif - - - /* The eMode parameter is always valid */ - assert( eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_WAL - || eMode==PAGER_JOURNALMODE_MEMORY ); - - /* This routine is only called from the OP_JournalMode opcode, and - ** the logic there will never allow a temporary file to be changed - ** to WAL mode. - */ - assert( pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL ); - - /* Do allow the journalmode of an in-memory database to be set to - ** anything other than MEMORY or OFF - */ - if( MEMDB ){ - assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); - if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ - eMode = eOld; - } - } - - if( eMode!=eOld ){ - - /* Change the journal mode. */ - assert( pPager->eState!=PAGER_ERROR ); - pPager->journalMode = (u8)eMode; - - /* When transistioning from TRUNCATE or PERSIST to any other journal - ** mode except WAL, unless the pager is in locking_mode=exclusive mode, - ** delete the journal file. - */ - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); - assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); - assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); - assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); - - assert( isOpen(pPager->fd) || pPager->exclusiveMode ); - if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ - - /* In this case we would like to delete the journal file. If it is - ** not possible, then that is not a problem. Deleting the journal file - ** here is an optimization only. - ** - ** Before deleting the journal file, obtain a RESERVED lock on the - ** database file. This ensures that the journal file is not deleted - ** while it is in use by some other client. - */ - sqlite4OsClose(pPager->jfd); - if( pPager->eLock>=RESERVED_LOCK ){ - sqlite4OsDelete(pPager->pVfs, pPager->zJournal, 0); - }else{ - int rc = SQLITE_OK; - int state = pPager->eState; - assert( state==PAGER_OPEN || state==PAGER_READER ); - if( state==PAGER_OPEN ){ - rc = sqlite4PagerSharedLock(pPager); - } - if( pPager->eState==PAGER_READER ){ - assert( rc==SQLITE_OK ); - rc = pagerLockDb(pPager, RESERVED_LOCK); - } - if( rc==SQLITE_OK ){ - sqlite4OsDelete(pPager->pVfs, pPager->zJournal, 0); - } - if( rc==SQLITE_OK && state==PAGER_READER ){ - pagerUnlockDb(pPager, SHARED_LOCK); - }else if( state==PAGER_OPEN ){ - pager_unlock(pPager); - } - assert( state==pPager->eState ); - } - } - } - - /* Return the new journal mode */ - return (int)pPager->journalMode; -} - -/* -** Return the current journal mode. -*/ -int sqlite4PagerGetJournalMode(Pager *pPager){ - return (int)pPager->journalMode; -} - -/* -** Return TRUE if the pager is in a state where it is OK to change the -** journalmode. Journalmode changes can only happen when the database -** is unmodified. -*/ -int sqlite4PagerOkToChangeJournalMode(Pager *pPager){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0; - if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0; - return 1; -} - -/* -** Get/set the size-limit used for persistent journal files. -** -** Setting the size limit to -1 means no limit is enforced. -** An attempt to set a limit smaller than -1 is a no-op. -*/ -i64 sqlite4PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ - if( iLimit>=-1 ){ - pPager->journalSizeLimit = iLimit; - } - return pPager->journalSizeLimit; -} - -/* -** Return a pointer to the pPager->pBackup variable. The backup module -** in backup.c maintains the content of this variable. This module -** uses it opaquely as an argument to sqlite4BackupRestart() and -** sqlite4BackupUpdate() only. -*/ -sqlite4_backup **sqlite4PagerBackupPtr(Pager *pPager){ - return &pPager->pBackup; -} - - -#endif /* SQLITE_OMIT_DISKIO */ DELETED src/pager.h Index: src/pager.h ================================================================== --- src/pager.h +++ /dev/null @@ -1,179 +0,0 @@ -/* -** 2001 September 15 -** -** 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 header file defines the interface that the sqlite page cache -** subsystem. The page cache subsystem reads and writes a file a page -** at a time and provides a journal for rollback. -*/ - -#ifndef _PAGER_H_ -#define _PAGER_H_ - -/* -** Default maximum size for persistent journal files. A negative -** value means no limit. This value may be overridden using the -** sqlite4PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". -*/ -#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT - #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 -#endif - -/* -** The type used to represent a page number. The first page in a file -** is called page 1. 0 is used to represent "not a page". -*/ -typedef u32 Pgno; - -/* -** Each open file is managed by a separate instance of the "Pager" structure. -*/ -typedef struct Pager Pager; - -/* -** Handle type for pages. -*/ -typedef struct PgHdr DbPage; - -/* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is -** reserved for working around a windows/posix incompatibility). It is -** used in the journal to signify that the remainder of the journal file -** is devoted to storing a master journal name - there are no more pages to -** roll back. See comments for function writeMasterJournal() in pager.c -** for details. -*/ -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) - -/* -** Allowed values for the flags parameter to sqlite4PagerOpen(). -** -** NOTE: These values must match the corresponding BTREE_ values in btree.h. -*/ -#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ -#define PAGER_MEMORY 0x0002 /* In-memory database */ - -/* -** Valid values for the second argument to sqlite4PagerLockingMode(). -*/ -#define PAGER_LOCKINGMODE_QUERY -1 -#define PAGER_LOCKINGMODE_NORMAL 0 -#define PAGER_LOCKINGMODE_EXCLUSIVE 1 - -/* -** Numeric constants that encode the journalmode. -*/ -#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ -#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ -#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ -#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ -#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ -#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ -#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ - -/* -** The remainder of this file contains the declarations of the functions -** that make up the Pager sub-system API. See source code comments for -** a detailed description of each routine. -*/ - -/* Open and close a Pager connection. */ -int sqlite4PagerOpen( - sqlite4_vfs*, - Pager **ppPager, - const char*, - int, - int, - int, - void(*)(DbPage*) -); -int sqlite4PagerClose(Pager *pPager); -int sqlite4PagerReadFileheader(Pager*, int, unsigned char*); - -/* Functions used to configure a Pager object. */ -void sqlite4PagerSetBusyhandler(Pager*, int(*)(void *), void *); -int sqlite4PagerSetPagesize(Pager*, u32*, int); -int sqlite4PagerMaxPageCount(Pager*, int); -void sqlite4PagerSetCachesize(Pager*, int); -void sqlite4PagerShrink(Pager*); -void sqlite4PagerSetSafetyLevel(Pager*,int,int,int); -int sqlite4PagerLockingMode(Pager *, int); -int sqlite4PagerSetJournalMode(Pager *, int); -int sqlite4PagerGetJournalMode(Pager*); -int sqlite4PagerOkToChangeJournalMode(Pager*); -i64 sqlite4PagerJournalSizeLimit(Pager *, i64); -sqlite4_backup **sqlite4PagerBackupPtr(Pager*); - -/* Functions used to obtain and release page references. */ -int sqlite4PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); -#define sqlite4PagerGet(A,B,C) sqlite4PagerAcquire(A,B,C,0) -DbPage *sqlite4PagerLookup(Pager *pPager, Pgno pgno); -void sqlite4PagerRef(DbPage*); -void sqlite4PagerUnref(DbPage*); - -/* Operations on page references. */ -int sqlite4PagerWrite(DbPage*); -void sqlite4PagerDontWrite(DbPage*); -int sqlite4PagerMovepage(Pager*,DbPage*,Pgno,int); -int sqlite4PagerPageRefcount(DbPage*); -void *sqlite4PagerGetData(DbPage *); -void *sqlite4PagerGetExtra(DbPage *); - -/* Functions used to manage pager transactions and savepoints. */ -void sqlite4PagerPagecount(Pager*, int*); -int sqlite4PagerBegin(Pager*, int exFlag, int); -int sqlite4PagerCommitPhaseOne(Pager*,const char *zMaster, int); -int sqlite4PagerExclusiveLock(Pager*); -int sqlite4PagerSync(Pager *pPager); -int sqlite4PagerCommitPhaseTwo(Pager*); -int sqlite4PagerRollback(Pager*); -int sqlite4PagerOpenSavepoint(Pager *pPager, int n); -int sqlite4PagerSavepoint(Pager *pPager, int op, int iSavepoint); -int sqlite4PagerSharedLock(Pager *pPager); - -int sqlite4PagerCheckpoint(Pager *pPager, int, int*, int*); -int sqlite4PagerWalSupported(Pager *pPager); -int sqlite4PagerWalCallback(Pager *pPager); -int sqlite4PagerOpenWal(Pager *pPager, int *pisOpen); -int sqlite4PagerCloseWal(Pager *pPager); - -/* Functions used to query pager state and configuration. */ -u8 sqlite4PagerIsreadonly(Pager*); -int sqlite4PagerRefcount(Pager*); -int sqlite4PagerMemUsed(Pager*); -const char *sqlite4PagerFilename(Pager*); -const sqlite4_vfs *sqlite4PagerVfs(Pager*); -sqlite4_file *sqlite4PagerFile(Pager*); -const char *sqlite4PagerJournalname(Pager*); -int sqlite4PagerNosync(Pager*); -void *sqlite4PagerTempSpace(Pager*); -int sqlite4PagerIsMemdb(Pager*); -void sqlite4PagerCacheStat(Pager *, int, int, int *); -void sqlite4PagerClearCache(Pager *); - -/* Functions used to truncate the database file. */ -void sqlite4PagerTruncateImage(Pager*,Pgno); - -/* Functions to support testing and debugging. */ -#if !defined(NDEBUG) || defined(SQLITE_TEST) - Pgno sqlite4PagerPagenumber(DbPage*); - int sqlite4PagerIswriteable(DbPage*); -#endif -#ifdef SQLITE_TEST - int *sqlite4PagerStats(Pager*); - void sqlite4PagerRefdump(Pager*); - void disable_simulated_io_errors(void); - void enable_simulated_io_errors(void); -#else -# define disable_simulated_io_errors() -# define enable_simulated_io_errors() -#endif - -#endif /* _PAGER_H_ */ DELETED src/pcache.c Index: src/pcache.c ================================================================== --- src/pcache.c +++ /dev/null @@ -1,619 +0,0 @@ -/* -** 2008 August 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements that page cache. -*/ -#include "sqliteInt.h" - -/* -** A complete page cache is an instance of this structure. -*/ -struct PCache { - PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ - PgHdr *pSynced; /* Last synced page in dirty page list */ - int nRef; /* Number of referenced pages */ - int szCache; /* Configured cache size */ - int szPage; /* Size of every page in this cache */ - int szExtra; /* Size of extra space for each page */ - int bPurgeable; /* True if pages are on backing store */ - int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ - void *pStress; /* Argument to xStress */ - sqlite4_pcache *pCache; /* Pluggable cache module */ - PgHdr *pPage1; /* Reference to page 1 */ -}; - -/* -** Some of the assert() macros in this code are too expensive to run -** even during normal debugging. Use them only rarely on long-running -** tests. Enable the expensive asserts using the -** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. -*/ -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -# define expensive_assert(X) assert(X) -#else -# define expensive_assert(X) -#endif - -/********************************** Linked List Management ********************/ - -#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) -/* -** Check that the pCache->pSynced variable is set correctly. If it -** is not, either fail an assert or return zero. Otherwise, return -** non-zero. This is only used in debugging builds, as follows: -** -** expensive_assert( pcacheCheckSynced(pCache) ); -*/ -static int pcacheCheckSynced(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){ - assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) ); - } - return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0); -} -#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ - -/* -** Remove page pPage from the list of dirty pages. -*/ -static void pcacheRemoveFromDirtyList(PgHdr *pPage){ - PCache *p = pPage->pCache; - - assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); - assert( pPage->pDirtyPrev || pPage==p->pDirty ); - - /* Update the PCache1.pSynced variable if necessary. */ - if( p->pSynced==pPage ){ - PgHdr *pSynced = pPage->pDirtyPrev; - while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ - pSynced = pSynced->pDirtyPrev; - } - p->pSynced = pSynced; - } - - if( pPage->pDirtyNext ){ - pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; - }else{ - assert( pPage==p->pDirtyTail ); - p->pDirtyTail = pPage->pDirtyPrev; - } - if( pPage->pDirtyPrev ){ - pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; - }else{ - assert( pPage==p->pDirty ); - p->pDirty = pPage->pDirtyNext; - } - pPage->pDirtyNext = 0; - pPage->pDirtyPrev = 0; - - expensive_assert( pcacheCheckSynced(p) ); -} - -/* -** Add page pPage to the head of the dirty list (PCache1.pDirty is set to -** pPage). -*/ -static void pcacheAddToDirtyList(PgHdr *pPage){ - PCache *p = pPage->pCache; - - assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); - - pPage->pDirtyNext = p->pDirty; - if( pPage->pDirtyNext ){ - assert( pPage->pDirtyNext->pDirtyPrev==0 ); - pPage->pDirtyNext->pDirtyPrev = pPage; - } - p->pDirty = pPage; - if( !p->pDirtyTail ){ - p->pDirtyTail = pPage; - } - if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ - p->pSynced = pPage; - } - expensive_assert( pcacheCheckSynced(p) ); -} - -/* -** Wrapper around the pluggable caches xUnpin method. If the cache is -** being used for an in-memory database, this function is a no-op. -*/ -static void pcacheUnpin(PgHdr *p){ - PCache *pCache = p->pCache; - if( pCache->bPurgeable ){ - if( p->pgno==1 ){ - pCache->pPage1 = 0; - } - sqlite4GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 0); - } -} - -/*************************************************** General Interfaces ****** -** -** Initialize and shutdown the page cache subsystem. Neither of these -** functions are threadsafe. -*/ -int sqlite4PcacheInitialize(void){ - if( sqlite4GlobalConfig.pcache2.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. */ - sqlite4PCacheSetDefault(); - } - return sqlite4GlobalConfig.pcache2.xInit(sqlite4GlobalConfig.pcache2.pArg); -} -void sqlite4PcacheShutdown(void){ - if( sqlite4GlobalConfig.pcache2.xShutdown ){ - /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ - sqlite4GlobalConfig.pcache2.xShutdown(sqlite4GlobalConfig.pcache2.pArg); - } -} - -/* -** Return the size in bytes of a PCache object. -*/ -int sqlite4PcacheSize(void){ return sizeof(PCache); } - -/* -** Create a new PCache object. Storage space to hold the object -** has already been allocated and is passed in as the p pointer. -** The caller discovers how much space needs to be allocated by -** calling sqlite4PcacheSize(). -*/ -void sqlite4PcacheOpen( - int szPage, /* Size of every page */ - int szExtra, /* Extra space associated with each page */ - int bPurgeable, /* True if pages are on backing store */ - int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ - void *pStress, /* Argument to xStress */ - PCache *p /* Preallocated space for the PCache */ -){ - memset(p, 0, sizeof(PCache)); - p->szPage = szPage; - p->szExtra = szExtra; - p->bPurgeable = bPurgeable; - p->xStress = xStress; - p->pStress = pStress; - p->szCache = 100; -} - -/* -** Change the page size for PCache object. The caller must ensure that there -** are no outstanding page references when this function is called. -*/ -void sqlite4PcacheSetPageSize(PCache *pCache, int szPage){ - assert( pCache->nRef==0 && pCache->pDirty==0 ); - if( pCache->pCache ){ - sqlite4GlobalConfig.pcache2.xDestroy(pCache->pCache); - pCache->pCache = 0; - pCache->pPage1 = 0; - } - pCache->szPage = szPage; -} - -/* -** Compute the number of pages of cache requested. -*/ -static int numberOfCachePages(PCache *p){ - if( p->szCache>=0 ){ - return p->szCache; - }else{ - return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); - } -} - -/* -** Try to obtain a page from the cache. -*/ -int sqlite4PcacheFetch( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number to obtain */ - int createFlag, /* If true, create page if it does not exist already */ - PgHdr **ppPage /* Write the page here */ -){ - sqlite4_pcache_page *pPage = 0; - PgHdr *pPgHdr = 0; - int eCreate; - - assert( pCache!=0 ); - assert( createFlag==1 || createFlag==0 ); - assert( pgno>0 ); - - /* If the pluggable cache (sqlite4_pcache*) has not been allocated, - ** allocate it now. - */ - if( !pCache->pCache && createFlag ){ - sqlite4_pcache *p; - p = sqlite4GlobalConfig.pcache2.xCreate( - pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable - ); - if( !p ){ - return SQLITE_NOMEM; - } - sqlite4GlobalConfig.pcache2.xCachesize(p, numberOfCachePages(pCache)); - pCache->pCache = p; - } - - eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); - if( pCache->pCache ){ - pPage = sqlite4GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); - } - - if( !pPage && eCreate==1 ){ - PgHdr *pPg; - - /* Find a dirty page to write-out and recycle. First try to find a - ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC - ** cleared), but if that is not possible settle for any other - ** unreferenced dirty page. - */ - expensive_assert( pcacheCheckSynced(pCache) ); - for(pPg=pCache->pSynced; - pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); - pPg=pPg->pDirtyPrev - ); - pCache->pSynced = pPg; - if( !pPg ){ - for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); - } - if( pPg ){ - int rc; -#ifdef SQLITE_LOG_CACHE_SPILL - sqlite4_log(SQLITE_FULL, - "spill page %d making room for %d - cache used: %d/%d", - pPg->pgno, pgno, - sqlite4GlobalConfig.pcache.xPagecount(pCache->pCache), - numberOfCachePages(pCache)); -#endif - rc = pCache->xStress(pCache->pStress, pPg); - if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ - return rc; - } - } - - pPage = sqlite4GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); - } - - if( pPage ){ - pPgHdr = (PgHdr *)pPage->pExtra; - - if( !pPgHdr->pPage ){ - memset(pPgHdr, 0, sizeof(PgHdr)); - pPgHdr->pPage = pPage; - pPgHdr->pData = pPage->pBuf; - pPgHdr->pExtra = (void *)&pPgHdr[1]; - memset(pPgHdr->pExtra, 0, pCache->szExtra); - pPgHdr->pCache = pCache; - pPgHdr->pgno = pgno; - } - assert( pPgHdr->pCache==pCache ); - assert( pPgHdr->pgno==pgno ); - assert( pPgHdr->pData==pPage->pBuf ); - assert( pPgHdr->pExtra==(void *)&pPgHdr[1] ); - - if( 0==pPgHdr->nRef ){ - pCache->nRef++; - } - pPgHdr->nRef++; - if( pgno==1 ){ - pCache->pPage1 = pPgHdr; - } - } - *ppPage = pPgHdr; - return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK; -} - -/* -** Decrement the reference count on a page. If the page is clean and the -** reference count drops to 0, then it is made elible for recycling. -*/ -void sqlite4PcacheRelease(PgHdr *p){ - assert( p->nRef>0 ); - p->nRef--; - if( p->nRef==0 ){ - PCache *pCache = p->pCache; - pCache->nRef--; - if( (p->flags&PGHDR_DIRTY)==0 ){ - pcacheUnpin(p); - }else{ - /* Move the page to the head of the dirty list. */ - pcacheRemoveFromDirtyList(p); - pcacheAddToDirtyList(p); - } - } -} - -/* -** Increase the reference count of a supplied page by 1. -*/ -void sqlite4PcacheRef(PgHdr *p){ - assert(p->nRef>0); - p->nRef++; -} - -/* -** Drop a page from the cache. There must be exactly one reference to the -** page. This function deletes that reference, so after it returns the -** page pointed to by p is invalid. -*/ -void sqlite4PcacheDrop(PgHdr *p){ - PCache *pCache; - assert( p->nRef==1 ); - if( p->flags&PGHDR_DIRTY ){ - pcacheRemoveFromDirtyList(p); - } - pCache = p->pCache; - pCache->nRef--; - if( p->pgno==1 ){ - pCache->pPage1 = 0; - } - sqlite4GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 1); -} - -/* -** Make sure the page is marked as dirty. If it isn't dirty already, -** make it so. -*/ -void sqlite4PcacheMakeDirty(PgHdr *p){ - p->flags &= ~PGHDR_DONT_WRITE; - assert( p->nRef>0 ); - if( 0==(p->flags & PGHDR_DIRTY) ){ - p->flags |= PGHDR_DIRTY; - pcacheAddToDirtyList( p); - } -} - -/* -** Make sure the page is marked as clean. If it isn't clean already, -** make it so. -*/ -void sqlite4PcacheMakeClean(PgHdr *p){ - if( (p->flags & PGHDR_DIRTY) ){ - pcacheRemoveFromDirtyList(p); - p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC); - if( p->nRef==0 ){ - pcacheUnpin(p); - } - } -} - -/* -** Make every page in the cache clean. -*/ -void sqlite4PcacheCleanAll(PCache *pCache){ - PgHdr *p; - while( (p = pCache->pDirty)!=0 ){ - sqlite4PcacheMakeClean(p); - } -} - -/* -** Clear the PGHDR_NEED_SYNC flag from all dirty pages. -*/ -void sqlite4PcacheClearSyncFlags(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirty; p; p=p->pDirtyNext){ - p->flags &= ~PGHDR_NEED_SYNC; - } - pCache->pSynced = pCache->pDirtyTail; -} - -/* -** Change the page number of page p to newPgno. -*/ -void sqlite4PcacheMove(PgHdr *p, Pgno newPgno){ - PCache *pCache = p->pCache; - assert( p->nRef>0 ); - assert( newPgno>0 ); - sqlite4GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); - p->pgno = newPgno; - if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ - pcacheRemoveFromDirtyList(p); - pcacheAddToDirtyList(p); - } -} - -/* -** Drop every cache entry whose page number is greater than "pgno". The -** caller must ensure that there are no outstanding references to any pages -** other than page 1 with a page number greater than pgno. -** -** If there is a reference to page 1 and the pgno parameter passed to this -** function is 0, then the data area associated with page 1 is zeroed, but -** the page object is not dropped. -*/ -void sqlite4PcacheTruncate(PCache *pCache, Pgno pgno){ - if( pCache->pCache ){ - PgHdr *p; - PgHdr *pNext; - for(p=pCache->pDirty; p; p=pNext){ - pNext = p->pDirtyNext; - /* This routine never gets call with a positive pgno except right - ** after sqlite4PcacheCleanAll(). So if there are dirty pages, - ** it must be that pgno==0. - */ - assert( p->pgno>0 ); - if( ALWAYS(p->pgno>pgno) ){ - assert( p->flags&PGHDR_DIRTY ); - sqlite4PcacheMakeClean(p); - } - } - if( pgno==0 && pCache->pPage1 ){ - memset(pCache->pPage1->pData, 0, pCache->szPage); - pgno = 1; - } - sqlite4GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); - } -} - -/* -** Close a cache. -*/ -void sqlite4PcacheClose(PCache *pCache){ - if( pCache->pCache ){ - sqlite4GlobalConfig.pcache2.xDestroy(pCache->pCache); - } -} - -/* -** Discard the contents of the cache. -*/ -void sqlite4PcacheClear(PCache *pCache){ - sqlite4PcacheTruncate(pCache, 0); -} - -/* -** Merge two lists of pages connected by pDirty and in pgno order. -** Do not both fixing the pDirtyPrev pointers. -*/ -static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ - PgHdr result, *pTail; - pTail = &result; - while( pA && pB ){ - if( pA->pgnopgno ){ - pTail->pDirty = pA; - pTail = pA; - pA = pA->pDirty; - }else{ - pTail->pDirty = pB; - pTail = pB; - pB = pB->pDirty; - } - } - if( pA ){ - pTail->pDirty = pA; - }else if( pB ){ - pTail->pDirty = pB; - }else{ - pTail->pDirty = 0; - } - return result.pDirty; -} - -/* -** Sort the list of pages in accending order by pgno. Pages are -** connected by pDirty pointers. The pDirtyPrev pointers are -** corrupted by this sort. -** -** Since there cannot be more than 2^31 distinct pages in a database, -** there cannot be more than 31 buckets required by the merge sorter. -** One extra bucket is added to catch overflow in case something -** ever changes to make the previous sentence incorrect. -*/ -#define N_SORT_BUCKET 32 -static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ - PgHdr *a[N_SORT_BUCKET], *p; - int i; - memset(a, 0, sizeof(a)); - while( pIn ){ - p = pIn; - pIn = p->pDirty; - p->pDirty = 0; - for(i=0; ALWAYS(ipDirty; p; p=p->pDirtyNext){ - p->pDirty = p->pDirtyNext; - } - return pcacheSortDirtyList(pCache->pDirty); -} - -/* -** Return the total number of referenced pages held by the cache. -*/ -int sqlite4PcacheRefCount(PCache *pCache){ - return pCache->nRef; -} - -/* -** Return the number of references to the page supplied as an argument. -*/ -int sqlite4PcachePageRefcount(PgHdr *p){ - return p->nRef; -} - -/* -** Return the total number of pages in the cache. -*/ -int sqlite4PcachePagecount(PCache *pCache){ - int nPage = 0; - if( pCache->pCache ){ - nPage = sqlite4GlobalConfig.pcache2.xPagecount(pCache->pCache); - } - return nPage; -} - -#ifdef SQLITE_TEST -/* -** Get the suggested cache-size value. -*/ -int sqlite4PcacheGetCachesize(PCache *pCache){ - return numberOfCachePages(pCache); -} -#endif - -/* -** Set the suggested cache-size value. -*/ -void sqlite4PcacheSetCachesize(PCache *pCache, int mxPage){ - pCache->szCache = mxPage; - if( pCache->pCache ){ - sqlite4GlobalConfig.pcache2.xCachesize(pCache->pCache, - numberOfCachePages(pCache)); - } -} - -/* -** Free up as much memory as possible from the page cache. -*/ -void sqlite4PcacheShrink(PCache *pCache){ - if( pCache->pCache ){ - sqlite4GlobalConfig.pcache2.xShrink(pCache->pCache); - } -} - -#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) -/* -** For all dirty pages currently in the cache, invoke the specified -** callback. This is only used if the SQLITE_CHECK_PAGES macro is -** defined. -*/ -void sqlite4PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){ - PgHdr *pDirty; - for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){ - xIter(pDirty); - } -} -#endif DELETED src/pcache.h Index: src/pcache.h ================================================================== --- src/pcache.h +++ /dev/null @@ -1,159 +0,0 @@ -/* -** 2008 August 05 -** -** 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 header file defines the interface that the sqlite page cache -** subsystem. -*/ - -#ifndef _PCACHE_H_ - -typedef struct PgHdr PgHdr; -typedef struct PCache PCache; - -/* -** Every page in the cache is controlled by an instance of the following -** structure. -*/ -struct PgHdr { - sqlite4_pcache_page *pPage; /* Pcache object page handle */ - void *pData; /* Page data */ - void *pExtra; /* Extra content */ - PgHdr *pDirty; /* Transient list of dirty pages */ - Pgno pgno; /* Page number for this page */ - Pager *pPager; /* The pager this page is part of */ -#ifdef SQLITE_CHECK_PAGES - u32 pageHash; /* Hash of page content */ -#endif - u16 flags; /* PGHDR flags defined below */ - - /********************************************************************** - ** Elements above are public. All that follows is private to pcache.c - ** and should not be accessed by other modules. - */ - i16 nRef; /* Number of users of this page */ - PCache *pCache; /* Cache that owns this page */ - - PgHdr *pDirtyNext; /* Next element in list of dirty pages */ - PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ -}; - -/* Bit values for PgHdr.flags */ -#define PGHDR_DIRTY 0x002 /* Page has changed */ -#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before - ** writing this page to the database */ -#define PGHDR_NEED_READ 0x008 /* Content is unread */ -#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ -#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ - -/* Initialize and shutdown the page cache subsystem */ -int sqlite4PcacheInitialize(void); -void sqlite4PcacheShutdown(void); - -/* Page cache buffer management: -** These routines implement SQLITE_CONFIG_PAGECACHE. -*/ -void sqlite4PCacheBufferSetup(void *, int sz, int n); - -/* Create a new pager cache. -** Under memory stress, invoke xStress to try to make pages clean. -** Only clean and unpinned pages can be reclaimed. -*/ -void sqlite4PcacheOpen( - int szPage, /* Size of every page */ - int szExtra, /* Extra space associated with each page */ - int bPurgeable, /* True if pages are on backing store */ - int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ - void *pStress, /* Argument to xStress */ - PCache *pToInit /* Preallocated space for the PCache */ -); - -/* Modify the page-size after the cache has been created. */ -void sqlite4PcacheSetPageSize(PCache *, int); - -/* Return the size in bytes of a PCache object. Used to preallocate -** storage space. -*/ -int sqlite4PcacheSize(void); - -/* One release per successful fetch. Page is pinned until released. -** Reference counted. -*/ -int sqlite4PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); -void sqlite4PcacheRelease(PgHdr*); - -void sqlite4PcacheDrop(PgHdr*); /* Remove page from cache */ -void sqlite4PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ -void sqlite4PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ -void sqlite4PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ - -/* Change a page number. Used by incr-vacuum. */ -void sqlite4PcacheMove(PgHdr*, Pgno); - -/* Remove all pages with pgno>x. Reset the cache if x==0 */ -void sqlite4PcacheTruncate(PCache*, Pgno x); - -/* Get a list of all dirty pages in the cache, sorted by page number */ -PgHdr *sqlite4PcacheDirtyList(PCache*); - -/* Reset and close the cache object */ -void sqlite4PcacheClose(PCache*); - -/* Clear flags from pages of the page cache */ -void sqlite4PcacheClearSyncFlags(PCache *); - -/* Discard the contents of the cache */ -void sqlite4PcacheClear(PCache*); - -/* Return the total number of outstanding page references */ -int sqlite4PcacheRefCount(PCache*); - -/* Increment the reference count of an existing page */ -void sqlite4PcacheRef(PgHdr*); - -int sqlite4PcachePageRefcount(PgHdr*); - -/* Return the total number of pages stored in the cache */ -int sqlite4PcachePagecount(PCache*); - -#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) -/* Iterate through all dirty pages currently stored in the cache. This -** interface is only available if SQLITE_CHECK_PAGES is defined when the -** library is built. -*/ -void sqlite4PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); -#endif - -/* Set and get the suggested cache-size for the specified pager-cache. -** -** If no global maximum is configured, then the system attempts to limit -** the total number of pages cached by purgeable pager-caches to the sum -** of the suggested cache-sizes. -*/ -void sqlite4PcacheSetCachesize(PCache *, int); -#ifdef SQLITE_TEST -int sqlite4PcacheGetCachesize(PCache *); -#endif - -/* Free up as much memory as possible from the page cache */ -void sqlite4PcacheShrink(PCache*); - -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* Try to return memory used by the pcache module to the main memory heap */ -int sqlite4PcacheReleaseMemory(int); -#endif - -#ifdef SQLITE_TEST -void sqlite4PcacheStats(int*,int*,int*,int*); -#endif - -void sqlite4PCacheSetDefault(void); - -#endif /* _PCACHE_H_ */ DELETED src/pcache1.c Index: src/pcache1.c ================================================================== --- src/pcache1.c +++ /dev/null @@ -1,1019 +0,0 @@ -/* -** 2008 November 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements the default page cache implementation (the -** sqlite4_pcache interface). It also contains part of the implementation -** of the SQLITE_CONFIG_PAGECACHE and sqlite4_release_memory() features. -** If the default page cache implementation is overriden, then neither of -** these two features are available. -*/ - -#include "sqliteInt.h" - -typedef struct PCache1 PCache1; -typedef struct PgHdr1 PgHdr1; -typedef struct PgFreeslot PgFreeslot; -typedef struct PGroup PGroup; - -/* 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 recycles pages more efficiently. -** -** 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 { - sqlite4_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ - unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ - unsigned int nMinPage; /* Sum of nMin for purgeable caches */ - unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ - unsigned int nCurrentPage; /* Number of purgeable pages allocated */ - 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 sqlite4_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 PGroup mutex must be held when accessing nMax. - */ - PGroup *pGroup; /* PGroup this cache belongs to */ - int szPage; /* Size of allocated pages in bytes */ - int szExtra; /* Size of extra space 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 PGroup mutex. - */ - unsigned int nRecyclable; /* Number of pages in the LRU list */ - unsigned int nPage; /* Total number of pages in apHash */ - unsigned int nHash; /* Number of slots in apHash[] */ - PgHdr1 **apHash; /* Hash table for fast lookup by key */ - - unsigned int iMaxKey; /* Largest key seen since xTruncate() */ -}; - -/* -** Each cache entry is represented by an instance of the following -** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of -** PgHdr1.pCache->szPage bytes is allocated directly before this structure -** in memory. -*/ -struct PgHdr1 { - sqlite4_pcache_page page; - unsigned int iKey; /* Key value (page number) */ - PgHdr1 *pNext; /* Next in hash table chain */ - PCache1 *pCache; /* Cache that currently owns this page */ - PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ - PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ -}; - -/* -** Free slots in the allocator used to divide up the buffer provided using -** the SQLITE_CONFIG_PAGECACHE mechanism. -*/ -struct PgFreeslot { - PgFreeslot *pNext; /* Next free slot */ -}; - -/* -** Global data used by this cache. -*/ -static SQLITE_WSD struct PCacheGlobal { - PGroup grp; /* The global PGroup for mode (2) */ - - /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The - ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all - ** fixed at sqlite4_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. */ - sqlite4_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; - -/* -** All code in this file should access the global structure above via the -** alias "pcache1". This ensures that the WSD emulation is used when -** compiling for systems that do not support real WSD. -*/ -#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) - -/* -** Macros to enter and leave the PCache LRU mutex. -*/ -#define pcache1EnterMutex(X) sqlite4_mutex_enter((X)->mutex) -#define pcache1LeaveMutex(X) sqlite4_mutex_leave((X)->mutex) - -/******************************************************************************/ -/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ - -/* -** This function is called during initialization if a static buffer is -** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE -** verb to sqlite4_config(). Parameter pBuf points to an allocation large -** enough to contain 'n' buffers of 'sz' bytes each. -** -** This routine is called from sqlite4_initialize() and so it is guaranteed -** to be serialized already. There is no need for further mutexing. -*/ -void sqlite4PCacheBufferSetup(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; - pcache1.pFree = p; - pBuf = (void*)&((char*)pBuf)[sz]; - } - pcache1.pEnd = pBuf; - } -} - -/* -** Malloc function used within this file to allocate space from the buffer -** configured using sqlite4_config(SQLITE_CONFIG_PAGECACHE) option. If no -** such buffer exists or there is no space left in it, this function falls -** back to sqlite4Malloc(). -** -** 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 = 0; - assert( sqlite4_mutex_notheld(pcache1.grp.mutex) ); - sqlite4StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - if( nByte<=pcache1.szSlot ){ - sqlite4_mutex_enter(pcache1.mutex); - p = (PgHdr1 *)pcache1.pFree; - if( p ){ - pcache1.pFree = pcache1.pFree->pNext; - pcache1.nFreeSlot--; - pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); - sqlite4StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); - } - sqlite4_mutex_leave(pcache1.mutex); - } - if( p==0 ){ - /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get - ** it from sqlite4Malloc instead. - */ - p = sqlite4Malloc(nByte); - if( p ){ - int sz = sqlite4MallocSize(p); - sqlite4_mutex_enter(pcache1.mutex); - sqlite4StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); - sqlite4_mutex_leave(pcache1.mutex); - } - sqlite4MemdebugSetType(p, MEMTYPE_PCACHE); - } - return p; -} - -/* -** Free an allocated buffer obtained from pcache1Alloc(). -*/ -static int pcache1Free(void *p){ - int nFreed = 0; - if( p==0 ) return 0; - if( p>=pcache1.pStart && ppNext = pcache1.pFree; - pcache1.pFree = pSlot; - pcache1.nFreeSlot++; - pcache1.bUnderPressure = pcache1.nFreeSlot=pcache1.pStart && ppGroup->mutex) ); - pcache1LeaveMutex(pCache->pGroup); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - pPg = pcache1Alloc(pCache->szPage); - p = sqlite4Malloc(sizeof(PgHdr1) + pCache->szExtra); - if( !pPg || !p ){ - pcache1Free(pPg); - sqlite4_free(p); - pPg = 0; - } -#else - pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); - p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; -#endif - pcache1EnterMutex(pCache->pGroup); - - if( pPg ){ - p->page.pBuf = pPg; - p->page.pExtra = &p[1]; - if( pCache->bPurgeable ){ - pCache->pGroup->nCurrentPage++; - } - return p; - } - return 0; -} - -/* -** Free a page object allocated by pcache1AllocPage(). -** -** The pointer is allowed to be NULL, which is prudent. But it turns out -** that the current implementation happens to never call this routine -** with a NULL pointer, so we mark the NULL test with ALWAYS(). -*/ -static void pcache1FreePage(PgHdr1 *p){ - if( ALWAYS(p) ){ - PCache1 *pCache = p->pCache; - assert( sqlite4_mutex_held(p->pCache->pGroup->mutex) ); - pcache1Free(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - sqlite4_free(p); -#endif - if( pCache->bPurgeable ){ - pCache->pGroup->nCurrentPage--; - } - } -} - -/* -** Malloc function used by SQLite to obtain space from the buffer configured -** using sqlite4_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer -** exists, this function falls back to sqlite4Malloc(). -*/ -void *sqlite4PageMalloc(int sz){ - return pcache1Alloc(sz); -} - -/* -** Free an allocated buffer obtained from sqlite4PageMalloc(). -*/ -void sqlite4PageFree(void *p){ - pcache1Free(p); -} - - -/* -** 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 but 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+pCache->szExtra)<=pcache1.szSlot ){ - return pcache1.bUnderPressure; - }else{ - return sqlite4HeapNearlyFull(); - } -} - -/******************************************************************************/ -/******** General Implementation Functions ************************************/ - -/* -** This function is used to resize the hash table used by the cache passed -** as the first argument. -** -** 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( sqlite4_mutex_held(p->pGroup->mutex) ); - - nNew = p->nHash*2; - if( nNew<256 ){ - nNew = 256; - } - - pcache1LeaveMutex(p->pGroup); - if( p->nHash ){ sqlite4BeginBenignMalloc(); } - apNew = (PgHdr1 **)sqlite4_malloc(sizeof(PgHdr1 *)*nNew); - if( p->nHash ){ sqlite4EndBenignMalloc(); } - pcache1EnterMutex(p->pGroup); - if( apNew ){ - memset(apNew, 0, sizeof(PgHdr1 *)*nNew); - for(i=0; inHash; i++){ - PgHdr1 *pPage; - PgHdr1 *pNext = p->apHash[i]; - while( (pPage = pNext)!=0 ){ - unsigned int h = pPage->iKey % nNew; - pNext = pPage->pNext; - pPage->pNext = apNew[h]; - apNew[h] = pPage; - } - } - sqlite4_free(p->apHash); - p->apHash = apNew; - p->nHash = nNew; - } - - return (p->apHash ? SQLITE_OK : SQLITE_NOMEM); -} - -/* -** This function is used internally to remove the page pPage from the -** 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 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){ - PCache1 *pCache; - PGroup *pGroup; - - if( pPage==0 ) return; - pCache = pPage->pCache; - pGroup = pCache->pGroup; - assert( sqlite4_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( pGroup->pLruHead==pPage ){ - pGroup->pLruHead = pPage->pLruNext; - } - if( pGroup->pLruTail==pPage ){ - pGroup->pLruTail = pPage->pLruPrev; - } - pPage->pLruNext = 0; - pPage->pLruPrev = 0; - pPage->pCache->nRecyclable--; - } -} - - -/* -** Remove the page supplied as an argument from the hash table -** (PCache1.apHash structure) that it is currently stored in. -** -** 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( sqlite4_mutex_held(pCache->pGroup->mutex) ); - h = pPage->iKey % pCache->nHash; - for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); - *pp = (*pp)->pNext; - - pCache->nPage--; -} - -/* -** If there are currently more than nMaxPage pages allocated, try -** to recycle pages to reduce the number allocated to nMaxPage. -*/ -static void pcache1EnforceMaxPage(PGroup *pGroup){ - assert( sqlite4_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); - } -} - -/* -** Discard all pages from cache pCache with a page number (key value) -** greater than or equal to iLimit. Any pinned pages that meet this -** criteria are unpinned before they are discarded. -** -** The PCache mutex must be held when this function is called. -*/ -static void pcache1TruncateUnsafe( - PCache1 *pCache, /* The cache to truncate */ - unsigned int iLimit /* Drop pages with this pgno or larger */ -){ - TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */ - unsigned int h; - assert( sqlite4_mutex_held(pCache->pGroup->mutex) ); - for(h=0; hnHash; h++){ - PgHdr1 **pp = &pCache->apHash[h]; - PgHdr1 *pPage; - while( (pPage = *pp)!=0 ){ - if( pPage->iKey>=iLimit ){ - pCache->nPage--; - *pp = pPage->pNext; - pcache1PinPage(pPage); - pcache1FreePage(pPage); - }else{ - pp = &pPage->pNext; - TESTONLY( nPage++; ) - } - } - } - assert( pCache->nPage==nPage ); -} - -/******************************************************************************/ -/******** sqlite4_pcache Methods **********************************************/ - -/* -** Implementation of the sqlite4_pcache.xInit method. -*/ -static int pcache1Init(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit==0 ); - memset(&pcache1, 0, sizeof(pcache1)); - if( sqlite4GlobalConfig.bCoreMutex ){ - pcache1.grp.mutex = sqlite4_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); - pcache1.mutex = sqlite4_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); - } - pcache1.grp.mxPinned = 10; - pcache1.isInit = 1; - return SQLITE_OK; -} - -/* -** Implementation of the sqlite4_pcache.xShutdown method. -** Note that the static mutex allocated in xInit does -** not need to be freed. -*/ -static void pcache1Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit!=0 ); - memset(&pcache1, 0, sizeof(pcache1)); -} - -/* -** Implementation of the sqlite4_pcache.xCreate method. -** -** Allocate a new cache. -*/ -static sqlite4_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ - 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 */ - - /* - ** 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 = sqlite4GlobalConfig.bCoreMutex>0; -#endif - - assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); - assert( szExtra < 300 ); - - sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; - pCache = (PCache1 *)sqlite4_malloc(sz); - if( pCache ){ - memset(pCache, 0, sz); - if( separateCache ){ - pGroup = (PGroup*)&pCache[1]; - pGroup->mxPinned = 10; - }else{ - pGroup = &pcache1.grp; - } - pCache->pGroup = pGroup; - pCache->szPage = szPage; - pCache->szExtra = szExtra; - pCache->bPurgeable = (bPurgeable ? 1 : 0); - if( bPurgeable ){ - pCache->nMin = 10; - pcache1EnterMutex(pGroup); - pGroup->nMinPage += pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pcache1LeaveMutex(pGroup); - } - } - return (sqlite4_pcache *)pCache; -} - -/* -** Implementation of the sqlite4_pcache.xCachesize method. -** -** Configure the cache_size limit for a cache. -*/ -static void pcache1Cachesize(sqlite4_pcache *p, int nMax){ - PCache1 *pCache = (PCache1 *)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - pcache1EnterMutex(pGroup); - pGroup->nMaxPage += (nMax - pCache->nMax); - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->nMax = nMax; - pCache->n90pct = pCache->nMax*9/10; - pcache1EnforceMaxPage(pGroup); - pcache1LeaveMutex(pGroup); - } -} - -/* -** Implementation of the sqlite4_pcache.xShrink method. -** -** Free up as much memory as possible. -*/ -static void pcache1Shrink(sqlite4_pcache *p){ - PCache1 *pCache = (PCache1*)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - int savedMaxPage; - pcache1EnterMutex(pGroup); - savedMaxPage = pGroup->nMaxPage; - pGroup->nMaxPage = 0; - pcache1EnforceMaxPage(pGroup); - pGroup->nMaxPage = savedMaxPage; - pcache1LeaveMutex(pGroup); - } -} - -/* -** Implementation of the sqlite4_pcache.xPagecount method. -*/ -static int pcache1Pagecount(sqlite4_pcache *p){ - int n; - PCache1 *pCache = (PCache1*)p; - pcache1EnterMutex(pCache->pGroup); - n = pCache->nPage; - pcache1LeaveMutex(pCache->pGroup); - return n; -} - -/* -** Implementation of the sqlite4_pcache.xFetch method. -** -** Fetch a page by key value. -** -** Whether or not a new page may be allocated by this function depends on -** the value of the createFlag argument. 0 means do not allocate a new -** page. 1 means allocate a new page if space is easily available. 2 -** means to try really hard to allocate a new page. -** -** For a non-purgeable cache (a cache used as the storage for an in-memory -** database) there is really no difference between createFlag 1 and 2. So -** the calling function (pcache.c) will never have a createFlag of 1 on -** a non-purgeable cache. -** -** There are three different approaches to obtaining space for a page, -** depending on the value of parameter createFlag (which may be 0, 1 or 2). -** -** 1. Regardless of the value of createFlag, the cache is searched for a -** copy of the requested page. If one is found, it is returned. -** -** 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, 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, 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: -** -** (a) The number of pages allocated for the cache is already -** PCache1.nMax, or -** -** (b) The number of pages allocated for all purgeable caches is -** 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. -** -** 5. Otherwise, allocate and return a new page buffer. -*/ -static sqlite4_pcache_page *pcache1Fetch( - sqlite4_pcache *p, - unsigned int iKey, - int createFlag -){ - unsigned int nPinned; - PCache1 *pCache = (PCache1 *)p; - PGroup *pGroup; - PgHdr1 *pPage = 0; - - assert( pCache->bPurgeable || createFlag!=1 ); - assert( pCache->bPurgeable || pCache->nMin==0 ); - assert( pCache->bPurgeable==0 || pCache->nMin==10 ); - assert( pCache->nMin==0 || pCache->bPurgeable ); - pcache1EnterMutex(pGroup = pCache->pGroup); - - /* 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; - } - - /* 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 */ - assert( pCache->nPage >= pCache->nRecyclable ); - nPinned = pCache->nPage - pCache->nRecyclable; - assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); - assert( pCache->n90pct == pCache->nMax*9/10 ); - if( createFlag==1 && ( - nPinned>=pGroup->mxPinned - || nPinned>=pCache->n90pct - || pcache1UnderMemoryPressure(pCache) - )){ - goto fetch_out; - } - - if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ - goto fetch_out; - } - - /* Step 4. Try to recycle a page. */ - if( pCache->bPurgeable && pGroup->pLruTail && ( - (pCache->nPage+1>=pCache->nMax) - || pGroup->nCurrentPage>=pGroup->nMaxPage - || pcache1UnderMemoryPressure(pCache) - )){ - PCache1 *pOther; - pPage = pGroup->pLruTail; - pcache1RemoveFromHash(pPage); - pcache1PinPage(pPage); - pOther = pPage->pCache; - - /* We want to verify that szPage and szExtra are the same for pOther - ** and pCache. Assert that we can verify this by comparing sums. */ - assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 ); - assert( pCache->szExtra<512 ); - assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 ); - assert( pOther->szExtra<512 ); - - if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){ - pcache1FreePage(pPage); - pPage = 0; - }else{ - pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); - } - } - - /* Step 5. If a usable page buffer has still not been found, - ** attempt to allocate a new one. - */ - if( !pPage ){ - if( createFlag==1 ) sqlite4BeginBenignMalloc(); - pPage = pcache1AllocPage(pCache); - if( createFlag==1 ) sqlite4EndBenignMalloc(); - } - - if( pPage ){ - unsigned int h = iKey % pCache->nHash; - pCache->nPage++; - pPage->iKey = iKey; - pPage->pNext = pCache->apHash[h]; - pPage->pCache = pCache; - pPage->pLruPrev = 0; - pPage->pLruNext = 0; - *(void **)pPage->page.pExtra = 0; - pCache->apHash[h] = pPage; - } - -fetch_out: - if( pPage && iKey>pCache->iMaxKey ){ - pCache->iMaxKey = iKey; - } - pcache1LeaveMutex(pGroup); - return &pPage->page; -} - - -/* -** Implementation of the sqlite4_pcache.xUnpin method. -** -** Mark a page as unpinned (eligible for asynchronous recycling). -*/ -static void pcache1Unpin( - sqlite4_pcache *p, - sqlite4_pcache_page *pPg, - int reuseUnlikely -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PGroup *pGroup = pCache->pGroup; - - assert( pPage->pCache==pCache ); - pcache1EnterMutex(pGroup); - - /* It is an error to call this function if the page is already - ** part of the PGroup LRU list. - */ - assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); - assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage ); - - if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){ - pcache1RemoveFromHash(pPage); - pcache1FreePage(pPage); - }else{ - /* Add the page to the PGroup LRU list. */ - if( pGroup->pLruHead ){ - pGroup->pLruHead->pLruPrev = pPage; - pPage->pLruNext = pGroup->pLruHead; - pGroup->pLruHead = pPage; - }else{ - pGroup->pLruTail = pPage; - pGroup->pLruHead = pPage; - } - pCache->nRecyclable++; - } - - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite4_pcache.xRekey method. -*/ -static void pcache1Rekey( - sqlite4_pcache *p, - sqlite4_pcache_page *pPg, - unsigned int iOld, - unsigned int iNew -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PgHdr1 **pp; - unsigned int h; - assert( pPage->iKey==iOld ); - assert( pPage->pCache==pCache ); - - pcache1EnterMutex(pCache->pGroup); - - h = iOld%pCache->nHash; - pp = &pCache->apHash[h]; - while( (*pp)!=pPage ){ - pp = &(*pp)->pNext; - } - *pp = pPage->pNext; - - h = iNew%pCache->nHash; - pPage->iKey = iNew; - pPage->pNext = pCache->apHash[h]; - pCache->apHash[h] = pPage; - if( iNew>pCache->iMaxKey ){ - pCache->iMaxKey = iNew; - } - - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite4_pcache.xTruncate method. -** -** Discard all unpinned pages in the cache with a page number equal to -** or greater than parameter iLimit. Any pinned pages with a page number -** equal to or greater than iLimit are implicitly unpinned. -*/ -static void pcache1Truncate(sqlite4_pcache *p, unsigned int iLimit){ - PCache1 *pCache = (PCache1 *)p; - pcache1EnterMutex(pCache->pGroup); - if( iLimit<=pCache->iMaxKey ){ - pcache1TruncateUnsafe(pCache, iLimit); - pCache->iMaxKey = iLimit-1; - } - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite4_pcache.xDestroy method. -** -** Destroy a cache allocated using pcache1Create(). -*/ -static void pcache1Destroy(sqlite4_pcache *p){ - PCache1 *pCache = (PCache1 *)p; - PGroup *pGroup = pCache->pGroup; - assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); - pcache1EnterMutex(pGroup); - pcache1TruncateUnsafe(pCache, 0); - assert( pGroup->nMaxPage >= pCache->nMax ); - pGroup->nMaxPage -= pCache->nMax; - assert( pGroup->nMinPage >= pCache->nMin ); - pGroup->nMinPage -= pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pcache1EnforceMaxPage(pGroup); - pcache1LeaveMutex(pGroup); - sqlite4_free(pCache->apHash); - sqlite4_free(pCache); -} - -/* -** This function is called during initialization (sqlite4_initialize()) to -** install the default pluggable cache module, assuming the user has not -** already provided an alternative. -*/ -void sqlite4PCacheSetDefault(void){ - static const sqlite4_pcache_methods2 defaultMethods = { - 1, /* iVersion */ - 0, /* pArg */ - pcache1Init, /* xInit */ - pcache1Shutdown, /* xShutdown */ - pcache1Create, /* xCreate */ - pcache1Cachesize, /* xCachesize */ - pcache1Pagecount, /* xPagecount */ - pcache1Fetch, /* xFetch */ - pcache1Unpin, /* xUnpin */ - pcache1Rekey, /* xRekey */ - pcache1Truncate, /* xTruncate */ - pcache1Destroy, /* xDestroy */ - pcache1Shrink /* xShrink */ - }; - sqlite4_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); -} - -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* -** This function is called to free superfluous dynamically allocated memory -** held by the pager system. Memory in use by any SQLite pager allocated -** by the current thread may be sqlite4_free()ed. -** -** nReq is the number of bytes of memory required. Once this much has -** been released, the function returns. The return value is the total number -** of bytes of memory released. -*/ -int sqlite4PcacheReleaseMemory(int nReq){ - int nFree = 0; - assert( sqlite4_mutex_notheld(pcache1.grp.mutex) ); - assert( sqlite4_mutex_notheld(pcache1.mutex) ); - if( pcache1.pStart==0 ){ - PgHdr1 *p; - pcache1EnterMutex(&pcache1.grp); - while( (nReq<0 || nFreepage.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - nFree += sqlite4MemSize(p); -#endif - pcache1PinPage(p); - pcache1RemoveFromHash(p); - pcache1FreePage(p); - } - pcache1LeaveMutex(&pcache1.grp); - } - return nFree; -} -#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ - -#ifdef SQLITE_TEST -/* -** This function is used by test procedures to inspect the internal state -** of the global cache. -*/ -void sqlite4PcacheStats( - int *pnCurrent, /* OUT: Total number of pages cached */ - int *pnMax, /* OUT: Global maximum cache size */ - int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ - int *pnRecyclable /* OUT: Total number of pages available for recycling */ -){ - PgHdr1 *p; - int nRecyclable = 0; - for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){ - nRecyclable++; - } - *pnCurrent = pcache1.grp.nCurrentPage; - *pnMax = (int)pcache1.grp.nMaxPage; - *pnMin = (int)pcache1.grp.nMinPage; - *pnRecyclable = nRecyclable; -} -#endif Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -12,25 +12,18 @@ ** This file contains code used to implement the PRAGMA command. */ #include "sqliteInt.h" /* -** Interpret the given string as a safety level. Return 0 for OFF, -** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or -** unrecognized string argument. -** -** Note that the values returned are one less that the values that -** should be passed into sqlite4BtreeSetSafetyLevel(). The is done -** to support legacy SQL code. The safety level used to be boolean -** and older scripts may have used numbers 0 for OFF and 1 for ON. +** Interpret the given string as a boolean value. */ -static u8 getSafetyLevel(const char *z){ - /* 123456789 123456789 */ - static const char zText[] = "onoffalseyestruefull"; - static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16}; - static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4}; - static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2}; +u8 sqlite4GetBoolean(const char *z){ + /* 123456789 12345 */ + static const char zText[] = "onoffalseyestrue"; + static const u8 iOffset[] = {0, 1, 2, 4, 9, 12}; + static const u8 iLength[] = {2, 2, 3, 5, 3, 4}; + static const u8 iValue[] = {1, 0, 0, 0, 1, 1}; int i, n; if( sqlite4Isdigit(*z) ){ return (u8)sqlite4Atoi(z); } n = sqlite4Strlen30(z); @@ -40,94 +33,16 @@ } } return 1; } -/* -** Interpret the given string as a boolean value. -*/ -u8 sqlite4GetBoolean(const char *z){ - return getSafetyLevel(z)&1; -} - /* The sqlite4GetBoolean() function is used by other modules but the ** remainder of this file is specific to PRAGMA processing. So omit ** the rest of the file if PRAGMAs are omitted from the build. */ #if !defined(SQLITE_OMIT_PRAGMA) -/* -** Interpret the given string as a locking mode value. -*/ -static int getLockingMode(const char *z){ - if( z ){ - if( 0==sqlite4StrICmp(z, "exclusive") ) return PAGER_LOCKINGMODE_EXCLUSIVE; - if( 0==sqlite4StrICmp(z, "normal") ) return PAGER_LOCKINGMODE_NORMAL; - } - return PAGER_LOCKINGMODE_QUERY; -} - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** Interpret the given string as a temp db location. Return 1 for file -** backed temporary databases, 2 for the Red-Black tree in memory database -** and 0 to use the compile-time default. -*/ -static int getTempStore(const char *z){ - if( z[0]>='0' && z[0]<='2' ){ - return z[0] - '0'; - }else if( sqlite4StrICmp(z, "file")==0 ){ - return 1; - }else if( sqlite4StrICmp(z, "memory")==0 ){ - return 2; - }else{ - return 0; - } -} -#endif /* SQLITE_PAGER_PRAGMAS */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** Invalidate temp storage, either when the temp storage is changed -** from default, or when 'file' and the temp_store_directory has changed -*/ -static int invalidateTempStorage(Parse *pParse){ - sqlite4 *db = pParse->db; - if( db->aDb[1].pBt!=0 ){ - if( !db->autoCommit || sqlite4BtreeIsInReadTrans(db->aDb[1].pBt) ){ - sqlite4ErrorMsg(pParse, "temporary storage cannot be changed " - "from within a transaction"); - return SQLITE_ERROR; - } - sqlite4BtreeClose(db->aDb[1].pBt); - db->aDb[1].pBt = 0; - sqlite4KVStoreClose(db->aDb[1].pKV); - db->aDb[1].pKV = 0; - sqlite4ResetInternalSchema(db, -1); - } - return SQLITE_OK; -} -#endif /* SQLITE_PAGER_PRAGMAS */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** If the TEMP database is open, close it and mark the database schema -** as needing reloading. This must be done when using the SQLITE_TEMP_STORE -** or DEFAULT_TEMP_STORE pragmas. -*/ -static int changeTempStorage(Parse *pParse, const char *zStorageType){ - int ts = getTempStore(zStorageType); - sqlite4 *db = pParse->db; - if( db->temp_store==ts ) return SQLITE_OK; - if( invalidateTempStorage( pParse ) != SQLITE_OK ){ - return SQLITE_ERROR; - } - db->temp_store = (u8)ts; - return SQLITE_OK; -} -#endif /* SQLITE_PAGER_PRAGMAS */ - /* ** Generate code to return a single integer value. */ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ Vdbe *v = sqlite4GetVdbe(pParse); @@ -151,44 +66,32 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ static const struct sPragmaType { const char *zName; /* Name of the pragma */ int mask; /* Mask for the db->flags value */ } aPragma[] = { - { "full_column_names", SQLITE_FullColNames }, - { "short_column_names", SQLITE_ShortColNames }, - { "count_changes", SQLITE_CountRows }, - { "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 }, -#endif #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, { "vdbe_trace", SQLITE_VdbeTrace }, + { "kv_trace", SQLITE_KvTrace }, + { "trace", SQLITE_SqlTrace | SQLITE_VdbeListing | + SQLITE_VdbeTrace | SQLITE_KvTrace }, #endif #ifndef SQLITE_OMIT_CHECK { "ignore_check_constraints", SQLITE_IgnoreChecks }, #endif /* The following is VERY experimental */ { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode }, - /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted - ** flag if there are any active statements. */ - { "read_uncommitted", SQLITE_ReadUncommitted }, - { "recursive_triggers", SQLITE_RecTriggers }, - /* This flag may only be set if both foreign-key and trigger support ** are present in the build. */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) { "foreign_keys", SQLITE_ForeignKeys }, #endif }; - int i; + int i, j; const struct sPragmaType *p; for(i=0, p=aPragma; izName)==0 ){ sqlite4 *db = pParse->db; Vdbe *v; @@ -215,10 +118,15 @@ ** compiler (eg. count_changes). So add an opcode to expire all ** compiled SQL statements after modifying a pragma value. */ sqlite4VdbeAddOp2(v, OP_Expire, 0, 0); } + for(j=0; jnDb; j++){ + if( db->aDb[j].pKV ){ + db->aDb[j].pKV->fTrace = (db->flags & SQLITE_KvTrace)!=0; + } + } } return 1; } } @@ -243,31 +151,10 @@ return zName; } #endif -/* -** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants -** defined in pager.h. This function returns the associated lowercase -** journal-mode name. -*/ -const char *sqlite4JournalModename(int eMode){ - static char * const azModeName[] = { - "delete", "persist", "off", "truncate", "memory" - }; - assert( PAGER_JOURNALMODE_DELETE==0 ); - assert( PAGER_JOURNALMODE_PERSIST==1 ); - assert( PAGER_JOURNALMODE_OFF==2 ); - assert( PAGER_JOURNALMODE_TRUNCATE==3 ); - assert( PAGER_JOURNALMODE_MEMORY==4 ); - assert( PAGER_JOURNALMODE_WAL==5 ); - assert( eMode>=0 && eMode<=ArraySize(azModeName) ); - - if( eMode==ArraySize(azModeName) ) return 0; - return azModeName[eMode]; -} - /* ** Process a pragma statement. ** ** Pragmas are of this form: ** @@ -325,392 +212,11 @@ zDb = pId2->n>0 ? pDb->zName : 0; if( sqlite4AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){ goto pragma_out; } -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) - /* - ** PRAGMA [database.]default_cache_size - ** PRAGMA [database.]default_cache_size=N - ** - ** The first form reports the current persistent setting for the - ** page cache size. The value returned is the maximum number of - ** pages in the page cache. The second form sets both the current - ** page cache size value and the persistent page cache size value - ** stored in the database file. - ** - ** Older versions of SQLite would set the default cache size to a - ** negative number to indicate synchronous=OFF. These days, synchronous - ** is always on by default regardless of the sign of the default cache - ** size. But continue to take the absolute value of the default cache - ** size of historical compatibility. - */ - if( sqlite4StrICmp(zLeft,"default_cache_size")==0 ){ - static const VdbeOpList getCacheSize[] = { - { OP_Transaction, 0, 0, 0}, /* 0 */ - { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ - { OP_IfPos, 1, 7, 0}, - { OP_Integer, 0, 2, 0}, - { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 7, 0}, - { OP_Integer, 0, 1, 0}, /* 6 */ - { OP_ResultRow, 1, 1, 0}, - }; - int addr; - if( sqlite4ReadSchema(pParse) ) goto pragma_out; - sqlite4VdbeUsesBtree(v, iDb); - if( !zRight ){ - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); - pParse->nMem += 2; - addr = sqlite4VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); - sqlite4VdbeChangeP1(v, addr, iDb); - sqlite4VdbeChangeP1(v, addr+1, iDb); - sqlite4VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); - }else{ - int size = sqlite4AbsInt32(sqlite4Atoi(zRight)); - sqlite4BeginWriteOperation(pParse, 0, iDb); - sqlite4VdbeAddOp2(v, OP_Integer, size, 1); - sqlite4VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); - pDb->pSchema->cache_size = size; - sqlite4BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - }else -#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */ - -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) - /* - ** PRAGMA [database.]page_size - ** PRAGMA [database.]page_size=N - ** - ** The first form reports the current setting for the - ** database page size in bytes. The second form sets the - ** database page size value. The value can only be set if - ** the database has not yet been created. - */ - if( sqlite4StrICmp(zLeft,"page_size")==0 ){ - Btree *pBt = pDb->pBt; - assert( pBt!=0 ); - if( !zRight ){ - int size = ALWAYS(pBt) ? sqlite4BtreeGetPageSize(pBt) : 0; - returnSingleInt(pParse, "page_size", size); - }else{ - /* Malloc may fail when setting the page-size, as there is an internal - ** buffer that the pager module resizes using sqlite4_realloc(). - */ - db->nextPagesize = sqlite4Atoi(zRight); - if( SQLITE_NOMEM==sqlite4BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ - db->mallocFailed = 1; - } - } - }else - - /* - ** PRAGMA [database.]secure_delete - ** PRAGMA [database.]secure_delete=ON/OFF - ** - ** The first form reports the current setting for the - ** secure_delete flag. The second form changes the secure_delete - ** flag setting and reports thenew value. - */ - if( sqlite4StrICmp(zLeft,"secure_delete")==0 ){ - Btree *pBt = pDb->pBt; - int b = -1; - assert( pBt!=0 ); - if( zRight ){ - b = sqlite4GetBoolean(zRight); - } - if( pId2->n==0 && b>=0 ){ - int ii; - for(ii=0; iinDb; ii++){ - sqlite4BtreeSecureDelete(db->aDb[ii].pBt, b); - } - } - b = sqlite4BtreeSecureDelete(pBt, b); - returnSingleInt(pParse, "secure_delete", b); - }else - - /* - ** PRAGMA [database.]locking_mode - ** PRAGMA [database.]locking_mode = (normal|exclusive) - */ - if( sqlite4StrICmp(zLeft,"locking_mode")==0 ){ - const char *zRet = "normal"; - int eMode = getLockingMode(zRight); - - if( pId2->n==0 && eMode==PAGER_LOCKINGMODE_QUERY ){ - /* Simple "PRAGMA locking_mode;" statement. This is a query for - ** the current default locking mode (which may be different to - ** the locking-mode of the main database). - */ - eMode = db->dfltLockMode; - }else{ - Pager *pPager; - if( pId2->n==0 ){ - /* This indicates that no database name was specified as part - ** of the PRAGMA command. In this case the locking-mode must be - ** set on all attached databases, as well as the main db file. - ** - ** Also, the sqlite4.dfltLockMode variable is set so that - ** any subsequently attached databases also use the specified - ** locking mode. - */ - int ii; - assert(pDb==&db->aDb[0]); - for(ii=2; iinDb; ii++){ - pPager = sqlite4BtreePager(db->aDb[ii].pBt); - sqlite4PagerLockingMode(pPager, eMode); - } - db->dfltLockMode = (u8)eMode; - } - pPager = sqlite4BtreePager(pDb->pBt); - eMode = sqlite4PagerLockingMode(pPager, eMode); - } - - assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE); - if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){ - zRet = "exclusive"; - } - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC); - sqlite4VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0); - sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 1); - }else - - /* - ** PRAGMA [database.]journal_mode - ** PRAGMA [database.]journal_mode = - ** (delete|persist|off|truncate|memory|wal|off) - */ - if( sqlite4StrICmp(zLeft,"journal_mode")==0 ){ - int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ - int ii; /* Loop counter */ - - /* Force the schema to be loaded on all databases. This causes all - ** database files to be opened and the journal_modes set. This is - ** necessary because subsequent processing must know if the databases - ** are in WAL mode. */ - if( sqlite4ReadSchema(pParse) ){ - goto pragma_out; - } - - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); - - if( zRight==0 ){ - /* If there is no "=MODE" part of the pragma, do a query for the - ** current mode */ - eMode = PAGER_JOURNALMODE_QUERY; - }else{ - const char *zMode; - int n = sqlite4Strlen30(zRight); - for(eMode=0; (zMode = sqlite4JournalModename(eMode))!=0; eMode++){ - if( sqlite4StrNICmp(zRight, zMode, n)==0 ) break; - } - if( !zMode ){ - /* If the "=MODE" part does not match any known journal mode, - ** then do a query */ - eMode = PAGER_JOURNALMODE_QUERY; - } - } - if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){ - /* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */ - iDb = 0; - pId2->n = 1; - } - for(ii=db->nDb-1; ii>=0; ii--){ - if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ - sqlite4VdbeUsesBtree(v, ii); - sqlite4VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode); - } - } - sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 1); - }else - - /* - ** PRAGMA [database.]journal_size_limit - ** PRAGMA [database.]journal_size_limit=N - ** - ** Get or set the size limit on rollback journal files. - */ - if( sqlite4StrICmp(zLeft,"journal_size_limit")==0 ){ - Pager *pPager = sqlite4BtreePager(pDb->pBt); - i64 iLimit = -2; - if( zRight ){ - sqlite4Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); - if( iLimit<-1 ) iLimit = -1; - } - iLimit = sqlite4PagerJournalSizeLimit(pPager, iLimit); - returnSingleInt(pParse, "journal_size_limit", iLimit); - }else - -#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ - - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS - /* - ** PRAGMA [database.]cache_size - ** PRAGMA [database.]cache_size=N - ** - ** The first form reports the current local setting for the - ** page cache size. The second form sets the local - ** page cache size value. If N is positive then that is the - ** number of pages in the cache. If N is negative, then the - ** number of pages is adjusted so that the cache uses -N kibibytes - ** of memory. - */ - if( sqlite4StrICmp(zLeft,"cache_size")==0 ){ - if( sqlite4ReadSchema(pParse) ) goto pragma_out; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); - if( !zRight ){ - returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); - }else{ - int size = sqlite4Atoi(zRight); - pDb->pSchema->cache_size = size; - sqlite4BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - }else - - /* - ** PRAGMA temp_store - ** PRAGMA temp_store = "default"|"memory"|"file" - ** - ** Return or set the local value of the temp_store flag. Changing - ** the local value does not make changes to the disk file and the default - ** value will be restored the next time the database is opened. - ** - ** Note that it is possible for the library compile-time options to - ** override this setting - */ - if( sqlite4StrICmp(zLeft, "temp_store")==0 ){ - if( !zRight ){ - returnSingleInt(pParse, "temp_store", db->temp_store); - }else{ - changeTempStorage(pParse, zRight); - } - }else - - /* - ** PRAGMA temp_store_directory - ** PRAGMA temp_store_directory = ""|"directory_name" - ** - ** Return or set the local value of the temp_store_directory flag. Changing - ** the value sets a specific directory to be used for temporary files. - ** Setting to a null string reverts to the default temporary directory search. - ** If temporary directory is changed, then invalidateTempStorage. - ** - */ - if( sqlite4StrICmp(zLeft, "temp_store_directory")==0 ){ - if( !zRight ){ - if( sqlite4_temp_directory ){ - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, - "temp_store_directory", SQLITE_STATIC); - sqlite4VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite4_temp_directory, 0); - sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else{ -#ifndef SQLITE_OMIT_WSD - if( zRight[0] ){ - int rc; - int res; - rc = sqlite4OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); - if( rc!=SQLITE_OK || res==0 ){ - sqlite4ErrorMsg(pParse, "not a writable directory"); - goto pragma_out; - } - } - if( SQLITE_TEMP_STORE==0 - || (SQLITE_TEMP_STORE==1 && db->temp_store<=1) - || (SQLITE_TEMP_STORE==2 && db->temp_store==1) - ){ - invalidateTempStorage(pParse); - } - sqlite4_free(sqlite4_temp_directory); - if( zRight[0] ){ - sqlite4_temp_directory = sqlite4_mprintf("%s", zRight); - }else{ - sqlite4_temp_directory = 0; - } -#endif /* SQLITE_OMIT_WSD */ - } - }else - -#if !defined(SQLITE_ENABLE_LOCKING_STYLE) -# if defined(__APPLE__) -# define SQLITE_ENABLE_LOCKING_STYLE 1 -# else -# define SQLITE_ENABLE_LOCKING_STYLE 0 -# endif -#endif -#if SQLITE_ENABLE_LOCKING_STYLE - /* - ** PRAGMA [database.]lock_proxy_file - ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path" - ** - ** Return or set the value of the lock_proxy_file flag. Changing - ** the value sets a specific file to be used for database access locks. - ** - */ - if( sqlite4StrICmp(zLeft, "lock_proxy_file")==0 ){ - if( !zRight ){ - Pager *pPager = sqlite4BtreePager(pDb->pBt); - char *proxy_file_path = NULL; - sqlite4_file *pFile = sqlite4PagerFile(pPager); - sqlite4OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE, - &proxy_file_path); - - if( proxy_file_path ){ - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, - "lock_proxy_file", SQLITE_STATIC); - sqlite4VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0); - sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else{ - Pager *pPager = sqlite4BtreePager(pDb->pBt); - sqlite4_file *pFile = sqlite4PagerFile(pPager); - int res; - if( zRight[0] ){ - res=sqlite4OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, - zRight); - } else { - res=sqlite4OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, - NULL); - } - if( res!=SQLITE_OK ){ - sqlite4ErrorMsg(pParse, "failed to set lock proxy file"); - goto pragma_out; - } - } - }else -#endif /* SQLITE_ENABLE_LOCKING_STYLE */ - - /* - ** PRAGMA [database.]synchronous - ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL - ** - ** Return or set the local value of the synchronous flag. Changing - ** the local value does not make changes to the disk file and the - ** default value will be restored the next time the database is - ** opened. - */ - if( sqlite4StrICmp(zLeft,"synchronous")==0 ){ - if( sqlite4ReadSchema(pParse) ) goto pragma_out; - if( !zRight ){ - returnSingleInt(pParse, "synchronous", pDb->safety_level-1); - }else{ - if( !db->autoCommit ){ - sqlite4ErrorMsg(pParse, - "Safety level may not be changed inside a transaction"); - }else{ - pDb->safety_level = getSafetyLevel(zRight)+1; - } - } - }else -#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ + #ifndef SQLITE_OMIT_FLAG_PRAGMAS if( flagPragma(pParse, zLeft, zRight) ){ /* The flagPragma() subroutine also generates any necessary code ** there is nothing more to do here */ @@ -826,16 +332,16 @@ pParse->nMem = 3; sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); sqlite4VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); sqlite4VdbeSetColName(v, 2, COLNAME_NAME, "file", SQLITE_STATIC); for(i=0; inDb; i++){ - if( db->aDb[i].pBt==0 ) continue; + if( db->aDb[i].pKV==0 ) continue; assert( db->aDb[i].zName!=0 ); sqlite4VdbeAddOp2(v, OP_Integer, i, 1); sqlite4VdbeAddOp4(v, OP_String8, 0, 2, 0, db->aDb[i].zName, 0); sqlite4VdbeAddOp4(v, OP_String8, 0, 3, 0, - sqlite4BtreeGetFilename(db->aDb[i].pBt), 0); + "filename", 0); sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 3); } }else if( sqlite4StrICmp(zLeft, "collation_list")==0 ){ @@ -923,173 +429,10 @@ #ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX # define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100 #endif -#ifndef SQLITE_OMIT_INTEGRITY_CHECK - /* Pragma "quick_check" is an experimental reduced version of - ** integrity_check designed to detect most database corruption - ** without most of the overhead of a full integrity-check. - */ - if( sqlite4StrICmp(zLeft, "integrity_check")==0 - || sqlite4StrICmp(zLeft, "quick_check")==0 - ){ - int i, j, addr, mxErr; - - /* Code that appears at the end of the integrity check. If no error - ** messages have been generated, output OK. Otherwise output the - ** error message - */ - static const VdbeOpList endCode[] = { - { OP_AddImm, 1, 0, 0}, /* 0 */ - { OP_IfNeg, 1, 0, 0}, /* 1 */ - { OP_String8, 0, 3, 0}, /* 2 */ - { OP_ResultRow, 3, 1, 0}, - }; - - int isQuick = (sqlite4Tolower(zLeft[0])=='q'); - - /* Initialize the VDBE program */ - if( sqlite4ReadSchema(pParse) ) goto pragma_out; - pParse->nMem = 6; - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC); - - /* Set the maximum error count */ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - if( zRight ){ - sqlite4GetInt32(zRight, &mxErr); - if( mxErr<=0 ){ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - } - } - sqlite4VdbeAddOp2(v, OP_Integer, mxErr, 1); /* reg[1] holds errors left */ - - /* Do an integrity check on each database file */ - for(i=0; inDb; i++){ - HashElem *x; - Hash *pTbls; - int cnt = 0; - - if( OMIT_TEMPDB && i==1 ) continue; - - sqlite4CodeVerifySchema(pParse, i); - addr = sqlite4VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */ - sqlite4VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite4VdbeJumpHere(v, addr); - - /* Do an integrity check of the B-Tree - ** - ** Begin by filling registers 2, 3, ... with the root pages numbers - ** for all tables and indices in the database. - */ - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); - pTbls = &db->aDb[i].pSchema->tblHash; - for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - sqlite4VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt); - cnt++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqlite4VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt); - cnt++; - } - } - - /* Make sure sufficient number of registers have been allocated */ - if( pParse->nMem < cnt+4 ){ - pParse->nMem = cnt+4; - } - - /* Do the b-tree integrity checks */ - sqlite4VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1); - sqlite4VdbeChangeP5(v, (u8)i); - addr = sqlite4VdbeAddOp1(v, OP_IsNull, 2); - sqlite4VdbeAddOp4(v, OP_String8, 0, 3, 0, - sqlite4MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName), - P4_DYNAMIC); - sqlite4VdbeAddOp3(v, OP_Move, 2, 4, 1); - sqlite4VdbeAddOp3(v, OP_Concat, 4, 3, 2); - sqlite4VdbeAddOp2(v, OP_ResultRow, 2, 1); - sqlite4VdbeJumpHere(v, addr); - - /* Make sure all the indices are constructed correctly. - */ - for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - int loopTop; - - if( pTab->pIndex==0 ) continue; - addr = sqlite4VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */ - sqlite4VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite4VdbeJumpHere(v, addr); - sqlite4OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead); - sqlite4VdbeAddOp2(v, OP_Integer, 0, 2); /* reg(2) will count entries */ - loopTop = sqlite4VdbeAddOp2(v, OP_Rewind, 1, 0); - sqlite4VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */ - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int jmp2; - int r1; - static const VdbeOpList idxErr[] = { - { OP_AddImm, 1, -1, 0}, - { OP_String8, 0, 3, 0}, /* 1 */ - { OP_Rowid, 1, 4, 0}, - { OP_String8, 0, 5, 0}, /* 3 */ - { OP_String8, 0, 6, 0}, /* 4 */ - { OP_Concat, 4, 3, 3}, - { OP_Concat, 5, 3, 3}, - { OP_Concat, 6, 3, 3}, - { OP_ResultRow, 3, 1, 0}, - { OP_IfPos, 1, 0, 0}, /* 9 */ - { OP_Halt, 0, 0, 0}, - }; - r1 = sqlite4GenerateIndexKey(pParse, pIdx, 1, 3, 0, 0); - jmp2 = sqlite4VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1); - addr = sqlite4VdbeAddOpList(v, ArraySize(idxErr), idxErr); - sqlite4VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); - sqlite4VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); - sqlite4VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT); - sqlite4VdbeJumpHere(v, addr+9); - sqlite4VdbeJumpHere(v, jmp2); - } - sqlite4VdbeAddOp2(v, OP_Next, 1, loopTop+1); - sqlite4VdbeJumpHere(v, loopTop); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - static const VdbeOpList cntIdx[] = { - { OP_Integer, 0, 3, 0}, - { OP_Rewind, 0, 0, 0}, /* 1 */ - { OP_AddImm, 3, 1, 0}, - { OP_Next, 0, 0, 0}, /* 3 */ - { OP_Eq, 2, 0, 3}, /* 4 */ - { OP_AddImm, 1, -1, 0}, - { OP_String8, 0, 2, 0}, /* 6 */ - { OP_String8, 0, 3, 0}, /* 7 */ - { OP_Concat, 3, 2, 2}, - { OP_ResultRow, 2, 1, 0}, - }; - addr = sqlite4VdbeAddOp1(v, OP_IfPos, 1); - sqlite4VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite4VdbeJumpHere(v, addr); - addr = sqlite4VdbeAddOpList(v, ArraySize(cntIdx), cntIdx); - sqlite4VdbeChangeP1(v, addr+1, j+2); - sqlite4VdbeChangeP2(v, addr+1, addr+4); - sqlite4VdbeChangeP1(v, addr+3, j+2); - sqlite4VdbeChangeP2(v, addr+3, addr+2); - sqlite4VdbeJumpHere(v, addr+4); - sqlite4VdbeChangeP4(v, addr+6, - "wrong # of entries in index ", P4_STATIC); - sqlite4VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT); - } - } - } - addr = sqlite4VdbeAddOpList(v, ArraySize(endCode), endCode); - sqlite4VdbeChangeP2(v, addr, -mxErr); - sqlite4VdbeJumpHere(v, addr+1); - sqlite4VdbeChangeP4(v, addr+2, "ok", P4_STATIC); - }else -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ #ifndef SQLITE_OMIT_UTF16 /* ** PRAGMA encoding ** PRAGMA encoding = "utf-8"|"utf-16"|"utf-16le"|"utf-16be" @@ -1160,82 +503,10 @@ } } }else #endif /* SQLITE_OMIT_UTF16 */ -#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - /* - ** PRAGMA [database.]schema_version - ** PRAGMA [database.]schema_version = - ** - ** PRAGMA [database.]user_version - ** PRAGMA [database.]user_version = - ** - ** The pragma's schema_version and user_version are used to set or get - ** the value of the schema-version and user-version, respectively. Both - ** the schema-version and the user-version are 32-bit signed integers - ** stored in the database header. - ** - ** The schema-cookie is usually only manipulated internally by SQLite. It - ** is incremented by SQLite whenever the database schema is modified (by - ** creating or dropping a table or index). The schema version is used by - ** SQLite each time a query is executed to ensure that the internal cache - ** of the schema used when compiling the SQL query matches the schema of - ** the database against which the compiled query is actually executed. - ** Subverting this mechanism by using "PRAGMA schema_version" to modify - ** the schema-version is potentially dangerous and may lead to program - ** crashes or database corruption. Use with caution! - ** - ** The user-version is not used internally by SQLite. It may be used by - ** applications for any purpose. - */ - if( sqlite4StrICmp(zLeft, "schema_version")==0 - || sqlite4StrICmp(zLeft, "user_version")==0 - || sqlite4StrICmp(zLeft, "freelist_count")==0 - ){ - int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ - sqlite4VdbeUsesBtree(v, iDb); - switch( zLeft[0] ){ - case 'f': case 'F': - iCookie = BTREE_FREE_PAGE_COUNT; - break; - case 's': case 'S': - iCookie = BTREE_SCHEMA_VERSION; - break; - default: - iCookie = BTREE_USER_VERSION; - break; - } - - if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){ - /* Write the specified cookie value */ - static const VdbeOpList setCookie[] = { - { OP_Transaction, 0, 1, 0}, /* 0 */ - { OP_Integer, 0, 1, 0}, /* 1 */ - { OP_SetCookie, 0, 0, 1}, /* 2 */ - }; - int addr = sqlite4VdbeAddOpList(v, ArraySize(setCookie), setCookie); - sqlite4VdbeChangeP1(v, addr, iDb); - sqlite4VdbeChangeP1(v, addr+1, sqlite4Atoi(zRight)); - sqlite4VdbeChangeP1(v, addr+2, iDb); - sqlite4VdbeChangeP2(v, addr+2, iCookie); - }else{ - /* Read the specified cookie value */ - static const VdbeOpList readCookie[] = { - { OP_Transaction, 0, 0, 0}, /* 0 */ - { OP_ReadCookie, 0, 1, 0}, /* 1 */ - { OP_ResultRow, 1, 1, 0} - }; - int addr = sqlite4VdbeAddOpList(v, ArraySize(readCookie), readCookie); - sqlite4VdbeChangeP1(v, addr, iDb); - sqlite4VdbeChangeP1(v, addr+1, iDb); - sqlite4VdbeChangeP3(v, addr+1, iCookie); - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); - } - }else -#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* ** PRAGMA compile_options ** @@ -1264,97 +535,13 @@ */ if( sqlite4StrICmp(zLeft, "shrink_memory")==0 ){ sqlite4_db_release_memory(db); }else -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - /* - ** Report the current state of file logs for all databases - */ - if( sqlite4StrICmp(zLeft, "lock_status")==0 ){ - static const char *const azLockName[] = { - "unlocked", "shared", "reserved", "pending", "exclusive" - }; - int i; - sqlite4VdbeSetNumCols(v, 2); - pParse->nMem = 2; - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "database", SQLITE_STATIC); - sqlite4VdbeSetColName(v, 1, COLNAME_NAME, "status", SQLITE_STATIC); - for(i=0; inDb; i++){ - Btree *pBt; - Pager *pPager; - const char *zState = "unknown"; - int j; - if( db->aDb[i].zName==0 ) continue; - sqlite4VdbeAddOp4(v, OP_String8, 0, 1, 0, db->aDb[i].zName, P4_STATIC); - pBt = db->aDb[i].pBt; - if( pBt==0 || (pPager = sqlite4BtreePager(pBt))==0 ){ - zState = "closed"; - }else if( sqlite4_file_control(db, i ? db->aDb[i].zName : 0, - SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){ - zState = azLockName[j]; - } - sqlite4VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC); - sqlite4VdbeAddOp2(v, OP_ResultRow, 1, 2); - } - - }else -#endif - -#ifdef SQLITE_HAS_CODEC - if( sqlite4StrICmp(zLeft, "key")==0 && zRight ){ - sqlite4_key(db, zRight, sqlite4Strlen30(zRight)); - }else - if( sqlite4StrICmp(zLeft, "rekey")==0 && zRight ){ - sqlite4_rekey(db, zRight, sqlite4Strlen30(zRight)); - }else - if( zRight && (sqlite4StrICmp(zLeft, "hexkey")==0 || - sqlite4StrICmp(zLeft, "hexrekey")==0) ){ - int i, h1, h2; - char zKey[40]; - for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){ - h1 += 9*(1&(h1>>6)); - h2 += 9*(1&(h2>>6)); - zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4); - } - if( (zLeft[3] & 0xf)==0xb ){ - sqlite4_key(db, zKey, i/2); - }else{ - sqlite4_rekey(db, zKey, i/2); - } - }else -#endif -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) - if( sqlite4StrICmp(zLeft, "activate_extensions")==0 ){ -#ifdef SQLITE_HAS_CODEC - if( sqlite4StrNICmp(zRight, "see-", 4)==0 ){ - sqlite4_activate_see(&zRight[4]); - } -#endif -#ifdef SQLITE_ENABLE_CEROD - if( sqlite4StrNICmp(zRight, "cerod-", 6)==0 ){ - sqlite4_activate_cerod(&zRight[6]); - } -#endif - }else -#endif - {/* Empty ELSE clause */} - - /* - ** Reset the safety level, in case the fullfsync flag or synchronous - ** setting changed. - */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS - if( db->autoCommit ){ - sqlite4BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level, - (db->flags&SQLITE_FullFSync)!=0, - (db->flags&SQLITE_CkptFullFSync)!=0); - } -#endif pragma_out: sqlite4DbFree(db, zLeft); sqlite4DbFree(db, zRight); } #endif /* SQLITE_OMIT_PRAGMA */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -131,16 +131,14 @@ ** auxiliary databases. Return one of the SQLITE_ error codes to ** indicate success or failure. */ static int sqlite4InitOne(sqlite4 *db, int iDb, char **pzErrMsg){ int rc; - int i; - int size; Table *pTab; Db *pDb; char const *azArg[4]; - int meta[5]; + unsigned int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName; int openedTransaction = 0; @@ -171,11 +169,10 @@ #endif assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pSchema ); assert( sqlite4_mutex_held(db->mutex) ); - assert( iDb==1 || sqlite4BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* zMasterSchema and zInitScript are set to point at the master schema ** and initialisation script appropriate for the database being ** initialised. zMasterName is the name of the master table. */ @@ -206,23 +203,22 @@ } /* Create a cursor to hold the database open */ pDb = &db->aDb[iDb]; - if( pDb->pBt==0 ){ + if( pDb->pKV==0 ){ if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){ DbSetProperty(db, 1, DB_SchemaLoaded); } return SQLITE_OK; } /* If there is not already a read-only (or read-write) transaction opened - ** on the b-tree database, open one now. If a transaction is opened, it + ** on the database, open one now. If a transaction is opened, it ** will be closed before this function returns. */ - sqlite4BtreeEnter(pDb->pBt); - if( !sqlite4BtreeIsInReadTrans(pDb->pBt) ){ - rc = sqlite4BtreeBeginTrans(pDb->pBt, 0); + if( pDb->pKV->iTransLevel==0 ){ + rc = sqlite4KVStoreBegin(pDb->pKV, 1); if( rc!=SQLITE_OK ){ sqlite4SetString(pzErrMsg, db, "%s", sqlite4ErrStr(rc)); goto initone_error_out; } openedTransaction = 1; @@ -230,90 +226,25 @@ /* Get the database meta information. ** ** Meta values are as follows: ** meta[0] Schema cookie. Changes with each schema change. - ** meta[1] File format of schema layer. - ** meta[2] Size of the page cache. - ** meta[3] Largest rootpage (auto/incr_vacuum mode) - ** meta[4] Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE - ** meta[5] User version - ** meta[6] Incremental vacuum mode + ** meta[1] unused + ** meta[2] unused + ** meta[3] unused + ** meta[4] unused + ** meta[5] unused + ** meta[6] unused ** meta[7] unused ** meta[8] unused ** meta[9] unused ** ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to ** the possible values of meta[4]. */ - for(i=0; ipBt, i+1, (u32 *)&meta[i]); - } - pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; - - /* If opening a non-empty database, check the text encoding. For the - ** main database, set sqlite4.enc to the encoding of the main database. - ** For an attached db, it is an error if the encoding is not the same - ** as sqlite4.enc. - */ - if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */ - if( iDb==0 ){ - u8 encoding; - /* If opening the main database, set ENC(db). */ - encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; - if( encoding==0 ) encoding = SQLITE_UTF8; - ENC(db) = encoding; - db->pDfltColl = sqlite4FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); - }else{ - /* If opening an attached database, the encoding much match ENC(db) */ - if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ - sqlite4SetString(pzErrMsg, db, "attached databases must use the same" - " text encoding as main database"); - rc = SQLITE_ERROR; - goto initone_error_out; - } - } - }else{ - DbSetProperty(db, iDb, DB_Empty); - } - pDb->pSchema->enc = ENC(db); - - if( pDb->pSchema->cache_size==0 ){ -#ifndef SQLITE_OMIT_DEPRECATED - size = sqlite4AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); - if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } - pDb->pSchema->cache_size = size; -#else - pDb->pSchema->cache_size = SQLITE_DEFAULT_CACHE_SIZE; -#endif - sqlite4BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - - /* - ** file_format==1 Version 3.0.0. - ** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN - ** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults - ** file_format==4 Version 3.3.0. // DESC indices. Boolean constants - */ - pDb->pSchema->file_format = (u8)meta[BTREE_FILE_FORMAT-1]; - if( pDb->pSchema->file_format==0 ){ - pDb->pSchema->file_format = 1; - } - if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){ - sqlite4SetString(pzErrMsg, db, "unsupported file format"); - rc = SQLITE_ERROR; - goto initone_error_out; - } - - /* Ticket #2804: When we open a database in the newer file format, - ** clear the legacy_file_format pragma flag so that a VACUUM will - ** not downgrade the database and thus invalidate any descending - ** indices that the user might have created. - */ - if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ - db->flags &= ~SQLITE_LegacyFileFmt; - } + sqlite4KVStoreGetMeta(pDb->pKV, 0, ArraySize(meta), meta); + pDb->pSchema->schema_cookie = meta[0]; /* Read the schema information out of the schema tables */ assert( db->init.busy ); { @@ -356,18 +287,16 @@ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; } /* Jump here for an error that occurs after successfully allocating - ** curMain and calling sqlite4BtreeEnter(). For an error that occurs - ** before that point, jump to error_out. + ** curMain. For an error that occurs before that point, jump to error_out. */ initone_error_out: if( openedTransaction ){ - sqlite4BtreeCommit(pDb->pBt); + sqlite4KVStoreCommit(pDb->pKV, 0); } - sqlite4BtreeLeave(pDb->pBt); error_out: if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; } @@ -453,18 +382,18 @@ assert( pParse->checkSchema ); assert( sqlite4_mutex_held(db->mutex) ); for(iDb=0; iDbnDb; iDb++){ int openedTransaction = 0; /* True if a transaction is opened */ - Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ - if( pBt==0 ) continue; + KVStore *pKV = db->aDb[iDb].pKV; /* Database to read cookie from */ + if( pKV==0 ) continue; /* If there is not already a read-only (or read-write) transaction opened ** on the b-tree database, open one now. If a transaction is opened, it ** will be closed immediately after reading the meta-value. */ - if( !sqlite4BtreeIsInReadTrans(pBt) ){ - rc = sqlite4BtreeBeginTrans(pBt, 0); + if( pKV->iTransLevel==0 ){ + rc = sqlite4KVStoreBegin(pKV, 1); if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; } if( rc!=SQLITE_OK ) return; openedTransaction = 1; @@ -471,20 +400,19 @@ } /* Read the schema cookie from the database. If it does not match the ** value stored as part of the in-memory schema representation, ** set Parse.rc to SQLITE_SCHEMA. */ - sqlite4BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); + sqlite4KVStoreGetMeta(pKV, 0, 1, (u32 *)&cookie); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ sqlite4ResetInternalSchema(db, iDb); pParse->rc = SQLITE_SCHEMA; } /* Close the transaction, if one was opened. */ if( openedTransaction ){ - sqlite4BtreeCommit(pBt); + sqlite4KVStoreCommit(pKV, 0); } } } /* @@ -545,47 +473,10 @@ pParse->pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); assert( !db->mallocFailed ); assert( sqlite4_mutex_held(db->mutex) ); - /* Check to verify that it is possible to get a read lock on all - ** database schemas. The inability to get a read lock indicates that - ** some other database connection is holding a write-lock, which in - ** turn means that the other connection has made uncommitted changes - ** to the schema. - ** - ** Were we to proceed and prepare the statement against the uncommitted - ** schema changes and if those schema changes are subsequently rolled - ** back and different changes are made in their place, then when this - ** prepared statement goes to run the schema cookie would fail to detect - ** the schema change. Disaster would follow. - ** - ** This thread is currently holding mutexes on all Btrees (because - ** of the sqlite4BtreeEnterAll() in sqlite4LockAndPrepare()) so it - ** is not possible for another thread to start a new schema change - ** while this routine is running. Hence, we do not need to hold - ** locks on the schema, we just need to make sure nobody else is - ** holding them. - ** - ** Note that setting READ_UNCOMMITTED overrides most lock detection, - ** but it does *not* override schema lock detection, so this all still - ** works even if READ_UNCOMMITTED is set. - */ - for(i=0; inDb; i++) { - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - assert( sqlite4BtreeHoldsMutex(pBt) ); - rc = sqlite4BtreeSchemaLocked(pBt); - if( rc ){ - const char *zDb = db->aDb[i].zName; - sqlite4Error(db, rc, "database schema is locked: %s", zDb); - testcase( db->flags & SQLITE_ReadUncommitted ); - goto end_prepare; - } - } - } - sqlite4VtabUnlockList(db); pParse->db = db; pParse->nQueryLoop = (double)1; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ @@ -696,17 +587,15 @@ *ppStmt = 0; if( !sqlite4SafetyCheckOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite4_mutex_enter(db->mutex); - sqlite4BtreeEnterAll(db); rc = sqlite4Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); if( rc==SQLITE_SCHEMA ){ sqlite4_finalize(*ppStmt); rc = sqlite4Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); } - sqlite4BtreeLeaveAll(db); sqlite4_mutex_leave(db->mutex); return rc; } /* Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -1187,11 +1187,10 @@ ExprList *pEList /* Expressions defining the result set */ ){ Vdbe *v = pParse->pVdbe; int i, j; sqlite4 *db = pParse->db; - int fullNames, shortNames; #ifndef SQLITE_OMIT_EXPLAIN /* If this is an EXPLAIN, skip this step */ if( pParse->explain ){ return; @@ -1198,12 +1197,10 @@ } #endif if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return; pParse->colNamesSet = 1; - fullNames = (db->flags & SQLITE_FullColNames)!=0; - shortNames = (db->flags & SQLITE_ShortColNames)!=0; sqlite4VdbeSetNumCols(v, pEList->nExpr); for(i=0; inExpr; i++){ Expr *p; p = pEList->a[i].pExpr; if( NEVER(p==0) ) continue; @@ -1224,20 +1221,11 @@ if( iCol<0 ){ zCol = "rowid"; }else{ zCol = pTab->aCol[iCol].zName; } - if( !shortNames && !fullNames ){ - sqlite4VdbeSetColName(v, i, COLNAME_NAME, - sqlite4DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); - }else if( fullNames ){ - char *zName = 0; - zName = sqlite4MPrintf(db, "%s.%s", pTab->zName, zCol); - sqlite4VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC); - }else{ - sqlite4VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); - } + sqlite4VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); }else{ sqlite4VdbeSetColName(v, i, COLNAME_NAME, sqlite4DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } @@ -1389,19 +1377,14 @@ ** the result set of that SELECT. */ Table *sqlite4ResultSetOfSelect(Parse *pParse, Select *pSelect){ Table *pTab; sqlite4 *db = pParse->db; - int savedFlags; - savedFlags = db->flags; - db->flags &= ~SQLITE_FullColNames; - db->flags |= SQLITE_ShortColNames; sqlite4SelectPrep(pParse, pSelect, 0); if( pParse->nErr ) return 0; while( pSelect->pPrior ) pSelect = pSelect->pPrior; - db->flags = savedFlags; pTab = sqlite4DbMallocZero(db, sizeof(Table) ); if( pTab==0 ){ return 0; } /* The sqlite4ResultSetOfSelect() is only used n contexts where lookaside @@ -1615,11 +1598,10 @@ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite4VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); - sqlite4VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. @@ -3315,12 +3297,10 @@ ** in the result set and expand them one by one. */ struct ExprList_item *a = pEList->a; ExprList *pNew = 0; int flags = pParse->db->flags; - int longNames = (flags & SQLITE_FullColNames)!=0 - && (flags & SQLITE_ShortColNames)==0; for(k=0; knExpr; k++){ Expr *pE = a[k].pExpr; assert( pE->op!=TK_DOT || pE->pRight!=0 ); if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ @@ -3388,18 +3368,14 @@ } } pRight = sqlite4Expr(db, TK_ID, zName); zColname = zName; zToFree = 0; - if( longNames || pTabList->nSrc>1 ){ + if( pTabList->nSrc>1 ){ Expr *pLeft; pLeft = sqlite4Expr(db, TK_ID, zTabName); pExpr = sqlite4PExpr(pParse, TK_DOT, pLeft, pRight, 0); - if( longNames ){ - zColname = sqlite4MPrintf(db, "%s.%s", zTabName, zName); - zToFree = zColname; - } }else{ pExpr = pRight; } pNew = sqlite4ExprListAppend(pParse, pNew, pExpr); sColname.z = zColname; @@ -4004,11 +3980,10 @@ KeyInfo *pKeyInfo; distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); addrDistinctIndex = sqlite4VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); - sqlite4VdbeChangeP5(v, BTREE_UNORDERED); }else{ distinct = addrDistinctIndex = -1; } /* Aggregate and non-aggregate queries are handled differently */ Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -1049,18 +1049,10 @@ sqlite4_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); sqlite4_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; - sqlite4_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; - sqlite4_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - fprintf(pArg->out, "Page cache hits: %d\n", iCur); - iHiwtr = iCur = -1; - sqlite4_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - fprintf(pArg->out, "Page cache misses: %d\n", iCur); - iHiwtr = iCur = -1; sqlite4_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite4_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); @@ -1357,11 +1349,10 @@ /* ** Text of a help message */ static char zHelp[] = - ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail ON|OFF Stop after hitting an error. Default OFF\n" ".databases List names and files of attached databases\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" @@ -1395,11 +1386,10 @@ ".output FILENAME Send output to FILENAME\n" ".output stdout Send output to the screen\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" - ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" ".separator STRING Change separator used by output mode and .import\n" ".show Show the current values for various settings\n" @@ -1406,11 +1396,10 @@ ".stats ON|OFF Turn stats on or off\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" ".timeout MS Try opening locked tables for MS milliseconds\n" - ".vfsname ?AUX? Print the name of the VFS stack\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" ; static char zTimerHelp[] = ".timer ON|OFF Turn the CPU timer measurement on or off\n" @@ -1434,11 +1423,11 @@ if( db==0 || SQLITE_OK!=sqlite4_errcode(db) ){ fprintf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite4_errmsg(db)); exit(1); } -#ifndef SQLITE_OMIT_LOAD_EXTENSION +#if 0 /*ndef SQLITE_OMIT_LOAD_EXTENSION*/ sqlite4_enable_load_extension(p->db, 1); #endif } } @@ -1504,12 +1493,12 @@ ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int do_meta_command(char *zLine, struct callback_data *p){ int i = 1; int nArg = 0; - int n, c; int rc = 0; + int c, n; char *azArg[50]; /* Parse the input line into tokens. */ while( zLine[i] && nArg=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){ - const char *zDestFile; - const char *zDb; - sqlite4 *pDest; - sqlite4_backup *pBackup; - if( nArg==2 ){ - zDestFile = azArg[1]; - zDb = "main"; - }else{ - zDestFile = azArg[2]; - zDb = azArg[1]; - } - rc = sqlite4_open(zDestFile, &pDest); - if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); - sqlite4_close(pDest); - return 1; - } - open_db(p); - pBackup = sqlite4_backup_init(pDest, "main", p->db, zDb); - if( pBackup==0 ){ - fprintf(stderr, "Error: %s\n", sqlite4_errmsg(pDest)); - sqlite4_close(pDest); - return 1; - } - while( (rc = sqlite4_backup_step(pBackup,100))==SQLITE_OK ){} - sqlite4_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = 0; - }else{ - fprintf(stderr, "Error: %s\n", sqlite4_errmsg(pDest)); - rc = 1; - } - sqlite4_close(pDest); - }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){ bail_on_error = booleanValue(azArg[1]); }else - if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); @@ -1887,11 +1839,11 @@ } } }else #endif -#ifndef SQLITE_OMIT_LOAD_EXTENSION +#if 0 /*ndef SQLITE_OMIT_LOAD_EXTENSION*/ if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){ const char *zFile, *zProc; char *zErrMsg = 0; zFile = azArg[1]; zProc = nArg>=3 ? azArg[2] : 0; @@ -2015,56 +1967,10 @@ rc = process_input(p, alt); fclose(alt); } }else - if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){ - const char *zSrcFile; - const char *zDb; - sqlite4 *pSrc; - sqlite4_backup *pBackup; - int nTimeout = 0; - - if( nArg==2 ){ - zSrcFile = azArg[1]; - zDb = "main"; - }else{ - zSrcFile = azArg[2]; - zDb = azArg[1]; - } - rc = sqlite4_open(zSrcFile, &pSrc); - if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); - sqlite4_close(pSrc); - return 1; - } - open_db(p); - pBackup = sqlite4_backup_init(p->db, zDb, pSrc, "main"); - if( pBackup==0 ){ - fprintf(stderr, "Error: %s\n", sqlite4_errmsg(p->db)); - sqlite4_close(pSrc); - return 1; - } - while( (rc = sqlite4_backup_step(pBackup,100))==SQLITE_OK - || rc==SQLITE_BUSY ){ - if( rc==SQLITE_BUSY ){ - if( nTimeout++ >= 3 ) break; - sqlite4_sleep(100); - } - } - sqlite4_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = 0; - }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - fprintf(stderr, "Error: source database is busy\n"); - rc = 1; - }else{ - fprintf(stderr, "Error: %s\n", sqlite4_errmsg(p->db)); - rc = 1; - } - sqlite4_close(pSrc); - }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); @@ -2359,22 +2265,10 @@ }else if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ printf("SQLite %s %s\n" /*extra-version-info*/, sqlite4_libversion(), sqlite4_sourceid()); - }else - - if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){ - const char *zDbName = nArg==2 ? azArg[1] : "main"; - char *zVfsName = 0; - if( p->db ){ - sqlite4_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); - if( zVfsName ){ - printf("%s\n", zVfsName); - sqlite4_free(zVfsName); - } - } }else if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){ int j; assert( nArg<=ArraySize(azArg) ); Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -3849,11 +3849,10 @@ #ifndef SQLITE_OMIT_DEPRECATED SQLITE_DEPRECATED int sqlite4_aggregate_count(sqlite4_context*); SQLITE_DEPRECATED int sqlite4_expired(sqlite4_stmt*); SQLITE_DEPRECATED int sqlite4_transfer_bindings(sqlite4_stmt*, sqlite4_stmt*); SQLITE_DEPRECATED int sqlite4_global_recover(void); -SQLITE_DEPRECATED void sqlite4_thread_cleanup(void); SQLITE_DEPRECATED int sqlite4_memory_alarm(void(*)(void*,sqlite4_int64,int),void*,sqlite4_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values @@ -4553,39 +4552,10 @@ sqlite4*, void(*)(void *,int ,char const *,char const *,sqlite4_int64), void* ); -/* -** CAPI3REF: Enable Or Disable Shared Pager Cache -** KEYWORDS: {shared cache} -** -** ^(This routine enables or disables the sharing of the database cache -** and schema data structures between [database connection | connections] -** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false.)^ -** -** ^Cache sharing is enabled and disabled for an entire process. -** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, -** sharing was enabled or disabled for each thread separately. -** -** ^(The cache sharing mode set by this interface effects all subsequent -** calls to [sqlite4_open()], [sqlite4_open_v2()], and [sqlite4_open16()]. -** Existing database connections continue use the sharing mode -** that was in effect at the time they were opened.)^ -** -** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise.)^ -** -** ^Shared cache is disabled by default. But this might change in -** future releases of SQLite. Applications that care about shared -** cache setting should set it explicitly. -** -** See Also: [SQLite Shared-Cache Mode] -*/ -int sqlite4_enable_shared_cache(int); - /* ** CAPI3REF: Attempt To Free Heap Memory ** ** ^The sqlite4_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations @@ -5127,219 +5097,10 @@ ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ -/* -** CAPI3REF: A Handle To An Open BLOB -** KEYWORDS: {BLOB handle} {BLOB handles} -** -** An instance of this object represents an open BLOB on which -** [sqlite4_blob_open | incremental BLOB I/O] can be performed. -** ^Objects of this type are created by [sqlite4_blob_open()] -** and destroyed by [sqlite4_blob_close()]. -** ^The [sqlite4_blob_read()] and [sqlite4_blob_write()] interfaces -** can be used to read or write small subsections of the BLOB. -** ^The [sqlite4_blob_bytes()] interface returns the size of the BLOB in bytes. -*/ -typedef struct sqlite4_blob sqlite4_blob; - -/* -** CAPI3REF: Open A BLOB For Incremental I/O -** -** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located -** in row iRow, column zColumn, table zTable in database zDb; -** in other words, the same BLOB that would be selected by: -** -**
-**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
-** 
)^ -** -** ^If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. ^If it is zero, the BLOB is opened for read access. -** ^It is not possible to open a column that is part of an index or primary -** key for writing. ^If [foreign key constraints] are enabled, it is -** not possible to open a column that is part of a [child key] for writing. -** -** ^Note that the database name is not the filename that contains -** the database but rather the symbolic name of the database that -** appears after the AS keyword when the database is connected using [ATTACH]. -** ^For the main database file, the database name is "main". -** ^For TEMP tables, the database name is "temp". -** -** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written -** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set -** to be a null pointer.)^ -** ^This function sets the [database connection] error code and message -** accessible via [sqlite4_errcode()] and [sqlite4_errmsg()] and related -** functions. ^Note that the *ppBlob variable is always initialized in a -** way that makes it safe to invoke [sqlite4_blob_close()] on *ppBlob -** regardless of the success or failure of this routine. -** -** ^(If the row that a BLOB handle points to is modified by an -** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects -** then the BLOB handle is marked as "expired". -** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on.)^ -** ^Calls to [sqlite4_blob_read()] and [sqlite4_blob_write()] for -** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. -** ^(Changes written into a BLOB prior to the BLOB expiring are not -** rolled back by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion.)^ -** -** ^Use the [sqlite4_blob_bytes()] interface to determine the size of -** the opened blob. ^The size of a blob may not be changed by this -** interface. Use the [UPDATE] SQL command to change the size of a -** blob. -** -** ^The [sqlite4_bind_zeroblob()] and [sqlite4_result_zeroblob()] interfaces -** and the built-in [zeroblob] SQL function can be used, if desired, -** to create an empty, zero-filled blob in which to read or write using -** this interface. -** -** To avoid a resource leak, every open [BLOB handle] should eventually -** be released by a call to [sqlite4_blob_close()]. -*/ -int sqlite4_blob_open( - sqlite4*, - const char *zDb, - const char *zTable, - const char *zColumn, - sqlite4_int64 iRow, - int flags, - sqlite4_blob **ppBlob -); - -/* -** CAPI3REF: Move a BLOB Handle to a New Row -** -** ^This function is used to move an existing blob handle so that it points -** to a different row of the same database table. ^The new row is identified -** by the rowid value passed as the second argument. Only the row can be -** changed. ^The database, table and column on which the blob handle is open -** remain the same. Moving an existing blob handle to a new row can be -** faster than closing the existing handle and opening a new one. -** -** ^(The new row must meet the same criteria as for [sqlite4_blob_open()] - -** it must exist and there must be either a blob or text value stored in -** the nominated column.)^ ^If the new row is not present in the table, or if -** it does not contain a blob or text value, or if another error occurs, an -** SQLite error code is returned and the blob handle is considered aborted. -** ^All subsequent calls to [sqlite4_blob_read()], [sqlite4_blob_write()] or -** [sqlite4_blob_reopen()] on an aborted blob handle immediately return -** SQLITE_ABORT. ^Calling [sqlite4_blob_bytes()] on an aborted blob handle -** always returns zero. -** -** ^This function sets the database handle error code and message. -*/ -SQLITE_EXPERIMENTAL int sqlite4_blob_reopen(sqlite4_blob *, sqlite4_int64); - -/* -** CAPI3REF: Close A BLOB Handle -** -** ^Closes an open [BLOB handle]. -** -** ^Closing a BLOB shall cause the current transaction to commit -** if there are no other BLOBs, no pending prepared statements, and the -** database connection is in [autocommit mode]. -** ^If any writes were made to the BLOB, they might be held in cache -** until the close operation if they will fit. -** -** ^(Closing the BLOB often forces the changes -** out to disk and so if any I/O errors occur, they will likely occur -** at the time when the BLOB is closed. Any errors that occur during -** closing are reported as a non-zero return value.)^ -** -** ^(The BLOB is closed unconditionally. Even if this routine returns -** an error code, the BLOB is still closed.)^ -** -** ^Calling this routine with a null pointer (such as would be returned -** by a failed call to [sqlite4_blob_open()]) is a harmless no-op. -*/ -int sqlite4_blob_close(sqlite4_blob *); - -/* -** CAPI3REF: Return The Size Of An Open BLOB -** -** ^Returns the size in bytes of the BLOB accessible via the -** successfully opened [BLOB handle] in its only argument. ^The -** incremental blob I/O routines can only read or overwriting existing -** blob content; they cannot change the size of a blob. -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite4_blob_open()] and which has not -** been closed by [sqlite4_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -*/ -int sqlite4_blob_bytes(sqlite4_blob *); - -/* -** CAPI3REF: Read Data From A BLOB Incrementally -** -** ^(This function is used to read data from an open [BLOB handle] into a -** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset.)^ -** -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is -** less than zero, [SQLITE_ERROR] is returned and no data is read. -** ^The size of the blob (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite4_blob_bytes()] interface. -** -** ^An attempt to read from an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. -** -** ^(On success, sqlite4_blob_read() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite4_blob_open()] and which has not -** been closed by [sqlite4_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite4_blob_write()]. -*/ -int sqlite4_blob_read(sqlite4_blob *, void *Z, int N, int iOffset); - -/* -** CAPI3REF: Write Data Into A BLOB Incrementally -** -** ^This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. ^N bytes of data are copied from the buffer Z -** into the open BLOB, starting at offset iOffset. -** -** ^If the [BLOB handle] passed as the first argument was not opened for -** writing (the flags parameter to [sqlite4_blob_open()] was zero), -** this function returns [SQLITE_READONLY]. -** -** ^This function may only modify the contents of the BLOB; it is -** not possible to increase the size of a BLOB using this API. -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. ^If N is -** less than zero [SQLITE_ERROR] is returned and no data is written. -** The size of the BLOB (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite4_blob_bytes()] interface. -** -** ^An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred -** before the [BLOB handle] expired are not rolled back by the -** expiration of the handle, though of course those changes might -** have been overwritten by the statement that expired the BLOB handle -** or by other independent statements. -** -** ^(On success, sqlite4_blob_write() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite4_blob_open()] and which has not -** been closed by [sqlite4_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite4_blob_read()]. -*/ -int sqlite4_blob_write(sqlite4_blob *, const void *z, int n, int iOffset); - /* ** CAPI3REF: Virtual File System Objects ** ** A virtual filesystem (VFS) is an [sqlite4_vfs] object ** that SQLite uses to interact @@ -5616,11 +5377,11 @@ #define SQLITE_MUTEX_FAST 0 #define SQLITE_MUTEX_RECURSIVE 1 #define SQLITE_MUTEX_STATIC_MASTER 2 #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite4_malloc() */ #define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite4BtreeOpen() */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* NOT USED */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite4_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite4PageMalloc() */ @@ -6233,215 +5994,10 @@ void (*xTruncate)(sqlite4_pcache*, unsigned iLimit); void (*xDestroy)(sqlite4_pcache*); }; -/* -** CAPI3REF: Online Backup Object -** -** The sqlite4_backup object records state information about an ongoing -** online backup operation. ^The sqlite4_backup object is created by -** a call to [sqlite4_backup_init()] and is destroyed by a call to -** [sqlite4_backup_finish()]. -** -** See Also: [Using the SQLite Online Backup API] -*/ -typedef struct sqlite4_backup sqlite4_backup; - -/* -** CAPI3REF: Online Backup API. -** -** The backup API copies the content of one database into another. -** It is useful either for creating backups of databases or -** for copying in-memory databases to or from persistent files. -** -** See Also: [Using the SQLite Online Backup API] -** -** ^SQLite holds a write transaction open on the destination database file -** for the duration of the backup operation. -** ^The source database is read-locked only while it is being read; -** it is not locked continuously for the entire backup operation. -** ^Thus, the backup may be performed on a live source database without -** preventing other database connections from -** reading or writing to the source database while the backup is underway. -** -** ^(To perform a backup operation: -**
    -**
  1. sqlite4_backup_init() is called once to initialize the -** backup, -**
  2. sqlite4_backup_step() is called one or more times to transfer -** the data between the two databases, and finally -**
  3. sqlite4_backup_finish() is called to release all resources -** associated with the backup operation. -**
)^ -** There should be exactly one call to sqlite4_backup_finish() for each -** successful call to sqlite4_backup_init(). -** -** [[sqlite4_backup_init()]] sqlite4_backup_init() -** -** ^The D and N arguments to sqlite4_backup_init(D,N,S,M) are the -** [database connection] associated with the destination database -** and the database name, respectively. -** ^The database name is "main" for the main database, "temp" for the -** temporary database, or the name specified after the AS keyword in -** an [ATTACH] statement for an attached database. -** ^The S and M arguments passed to -** sqlite4_backup_init(D,N,S,M) identify the [database connection] -** and database name of the source database, respectively. -** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite4_backup_init(D,N,S,M) will fail with -** an error. -** -** ^If an error occurs within sqlite4_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are stored in the -** destination [database connection] D. -** ^The error code and message for the failed call to sqlite4_backup_init() -** can be retrieved using the [sqlite4_errcode()], [sqlite4_errmsg()], and/or -** [sqlite4_errmsg16()] functions. -** ^A successful call to sqlite4_backup_init() returns a pointer to an -** [sqlite4_backup] object. -** ^The [sqlite4_backup] object may be used with the sqlite4_backup_step() and -** sqlite4_backup_finish() functions to perform the specified backup -** operation. -** -** [[sqlite4_backup_step()]] sqlite4_backup_step() -** -** ^Function sqlite4_backup_step(B,N) will copy up to N pages between -** the source and destination databases specified by [sqlite4_backup] object B. -** ^If N is negative, all remaining source pages are copied. -** ^If sqlite4_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function returns [SQLITE_OK]. -** ^If sqlite4_backup_step(B,N) successfully finishes copying all pages -** from source to destination, then it returns [SQLITE_DONE]. -** ^If an error occurs while running sqlite4_backup_step(B,N), -** then an [error code] is returned. ^As well as [SQLITE_OK] and -** [SQLITE_DONE], a call to sqlite4_backup_step() may return [SQLITE_READONLY], -** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. -** -** ^(The sqlite4_backup_step() might return [SQLITE_READONLY] if -**
    -**
  1. the destination database was opened read-only, or -**
  2. the destination database is using write-ahead-log journaling -** and the destination and source page sizes differ, or -**
  3. the destination database is an in-memory database and the -** destination and source page sizes differ. -**
)^ -** -** ^If sqlite4_backup_step() cannot obtain a required file-system lock, then -** the [sqlite4_busy_handler | busy-handler function] -** is invoked (if one is specified). ^If the -** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. ^In this case the call to -** sqlite4_backup_step() can be retried later. ^If the source -** [database connection] -** is being used to write to the source database when sqlite4_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this -** case the call to sqlite4_backup_step() can be retried later on. ^(If -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or -** [SQLITE_READONLY] is returned, then -** there is no point in retrying the call to sqlite4_backup_step(). These -** errors are considered fatal.)^ The application must accept -** that the backup operation has failed and pass the backup operation handle -** to the sqlite4_backup_finish() to release associated resources. -** -** ^The first call to sqlite4_backup_step() obtains an exclusive lock -** on the destination file. ^The exclusive lock is not released until either -** sqlite4_backup_finish() is called or the backup operation is complete -** and sqlite4_backup_step() returns [SQLITE_DONE]. ^Every call to -** sqlite4_backup_step() obtains a [shared lock] on the source database that -** lasts for the duration of the sqlite4_backup_step() call. -** ^Because the source database is not locked between calls to -** sqlite4_backup_step(), the source database may be modified mid-way -** through the backup process. ^If the source database is modified by an -** external process or via a database connection other than the one being -** used by the backup operation, then the backup will be automatically -** restarted by the next call to sqlite4_backup_step(). ^If the source -** database is modified by the using the same database connection as is used -** by the backup operation, then the backup database is automatically -** updated at the same time. -** -** [[sqlite4_backup_finish()]] sqlite4_backup_finish() -** -** When sqlite4_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the application -** should destroy the [sqlite4_backup] by passing it to sqlite4_backup_finish(). -** ^The sqlite4_backup_finish() interfaces releases all -** resources associated with the [sqlite4_backup] object. -** ^If sqlite4_backup_step() has not yet returned [SQLITE_DONE], then any -** active write-transaction on the destination database is rolled back. -** The [sqlite4_backup] object is invalid -** and may not be used following a call to sqlite4_backup_finish(). -** -** ^The value returned by sqlite4_backup_finish is [SQLITE_OK] if no -** sqlite4_backup_step() errors occurred, regardless or whether or not -** sqlite4_backup_step() completed. -** ^If an out-of-memory condition or IO error occurred during any prior -** sqlite4_backup_step() call on the same [sqlite4_backup] object, then -** sqlite4_backup_finish() returns the corresponding [error code]. -** -** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite4_backup_step() -** is not a permanent error and does not affect the return value of -** sqlite4_backup_finish(). -** -** [[sqlite4_backup__remaining()]] [[sqlite4_backup_pagecount()]] -** sqlite4_backup_remaining() and sqlite4_backup_pagecount() -** -** ^Each call to sqlite4_backup_step() sets two values inside -** the [sqlite4_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite4_backup_remaining() and sqlite4_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite4_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. -** -** Concurrent Usage of Database Handles -** -** ^The source [database connection] may be used by the application for other -** purposes while a backup operation is underway or being initialized. -** ^If SQLite is compiled and configured to support threadsafe database -** connections, then the source database connection may be used concurrently -** from within other threads. -** -** However, the application must guarantee that the destination -** [database connection] is not passed to any other API (by any thread) after -** sqlite4_backup_init() is called and before the corresponding call to -** sqlite4_backup_finish(). SQLite does not currently check to see -** if the application incorrectly accesses the destination [database connection] -** and so no error code is reported, but the operations may malfunction -** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. -** -** If running in [shared cache mode], the application must -** guarantee that the shared cache used by the destination database -** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the disk file being -** backed up to is not accessed by any connection within the process, -** not just the specific connection that was passed to sqlite4_backup_init(). -** -** The [sqlite4_backup] object itself is partially threadsafe. Multiple -** threads may safely make multiple concurrent calls to sqlite4_backup_step(). -** However, the sqlite4_backup_remaining() and sqlite4_backup_pagecount() -** APIs are not strictly speaking threadsafe. If they are invoked at the -** same time as another thread is invoking sqlite4_backup_step() it is -** possible that they return invalid values. -*/ -sqlite4_backup *sqlite4_backup_init( - sqlite4 *pDest, /* Destination database handle */ - const char *zDestName, /* Destination database name */ - sqlite4 *pSource, /* Source database handle */ - const char *zSourceName /* Source database name */ -); -int sqlite4_backup_step(sqlite4_backup *p, int nPage); -int sqlite4_backup_finish(sqlite4_backup *p); -int sqlite4_backup_remaining(sqlite4_backup *p); -int sqlite4_backup_pagecount(sqlite4_backup *p); - /* ** CAPI3REF: Unlock Notification ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or DELETED src/sqlite4ext.h Index: src/sqlite4ext.h ================================================================== --- src/sqlite4ext.h +++ /dev/null @@ -1,442 +0,0 @@ -/* -** 2006 June 7 -** -** 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 header file defines the SQLite interface for use by -** shared libraries that want to be imported as extensions into -** an SQLite instance. Shared libraries that intend to be loaded -** as extensions by SQLite should #include this file instead of -** sqlite4.h. -*/ -#ifndef _SQLITE3EXT_H_ -#define _SQLITE3EXT_H_ -#include "sqlite4.h" - -typedef struct sqlite4_api_routines sqlite4_api_routines; - -/* -** The following structure holds pointers to all of the SQLite API -** routines. -** -** WARNING: In order to maintain backwards compatibility, add new -** interfaces to the end of this structure only. If you insert new -** interfaces in the middle of this structure, then older different -** versions of SQLite will not be able to load each others' shared -** libraries! -*/ -struct sqlite4_api_routines { - void * (*aggregate_context)(sqlite4_context*,int nBytes); - int (*aggregate_count)(sqlite4_context*); - int (*bind_blob)(sqlite4_stmt*,int,const void*,int n,void(*)(void*)); - int (*bind_double)(sqlite4_stmt*,int,double); - int (*bind_int)(sqlite4_stmt*,int,int); - int (*bind_int64)(sqlite4_stmt*,int,sqlite_int64); - int (*bind_null)(sqlite4_stmt*,int); - int (*bind_parameter_count)(sqlite4_stmt*); - int (*bind_parameter_index)(sqlite4_stmt*,const char*zName); - const char * (*bind_parameter_name)(sqlite4_stmt*,int); - int (*bind_text)(sqlite4_stmt*,int,const char*,int n,void(*)(void*)); - int (*bind_text16)(sqlite4_stmt*,int,const void*,int,void(*)(void*)); - int (*bind_value)(sqlite4_stmt*,int,const sqlite4_value*); - int (*busy_handler)(sqlite4*,int(*)(void*,int),void*); - int (*busy_timeout)(sqlite4*,int ms); - int (*changes)(sqlite4*); - int (*close)(sqlite4*); - int (*collation_needed)(sqlite4*,void*,void(*)(void*,sqlite4*, - int eTextRep,const char*)); - int (*collation_needed16)(sqlite4*,void*,void(*)(void*,sqlite4*, - int eTextRep,const void*)); - const void * (*column_blob)(sqlite4_stmt*,int iCol); - int (*column_bytes)(sqlite4_stmt*,int iCol); - int (*column_bytes16)(sqlite4_stmt*,int iCol); - int (*column_count)(sqlite4_stmt*pStmt); - const char * (*column_database_name)(sqlite4_stmt*,int); - const void * (*column_database_name16)(sqlite4_stmt*,int); - const char * (*column_decltype)(sqlite4_stmt*,int i); - const void * (*column_decltype16)(sqlite4_stmt*,int); - double (*column_double)(sqlite4_stmt*,int iCol); - int (*column_int)(sqlite4_stmt*,int iCol); - sqlite_int64 (*column_int64)(sqlite4_stmt*,int iCol); - const char * (*column_name)(sqlite4_stmt*,int); - const void * (*column_name16)(sqlite4_stmt*,int); - const char * (*column_origin_name)(sqlite4_stmt*,int); - const void * (*column_origin_name16)(sqlite4_stmt*,int); - const char * (*column_table_name)(sqlite4_stmt*,int); - const void * (*column_table_name16)(sqlite4_stmt*,int); - const unsigned char * (*column_text)(sqlite4_stmt*,int iCol); - const void * (*column_text16)(sqlite4_stmt*,int iCol); - int (*column_type)(sqlite4_stmt*,int iCol); - sqlite4_value* (*column_value)(sqlite4_stmt*,int iCol); - void * (*commit_hook)(sqlite4*,int(*)(void*),void*); - int (*complete)(const char*sql); - int (*complete16)(const void*sql); - int (*create_collation)(sqlite4*,const char*,int,void*, - int(*)(void*,int,const void*,int,const void*), - int(*)(void*,int,const void*,int,const void*), - void(*)(void*)); - int (*create_function)(sqlite4*,const char*,int,int,void*, - void (*xFunc)(sqlite4_context*,int,sqlite4_value**), - void (*xStep)(sqlite4_context*,int,sqlite4_value**), - void (*xFinal)(sqlite4_context*)); - int (*create_function16)(sqlite4*,const void*,int,int,void*, - void (*xFunc)(sqlite4_context*,int,sqlite4_value**), - void (*xStep)(sqlite4_context*,int,sqlite4_value**), - void (*xFinal)(sqlite4_context*)); - int (*create_module)(sqlite4*,const char*,const sqlite4_module*,void*); - int (*data_count)(sqlite4_stmt*pStmt); - sqlite4 * (*db_handle)(sqlite4_stmt*); - int (*declare_vtab)(sqlite4*,const char*); - int (*enable_shared_cache)(int); - int (*errcode)(sqlite4*db); - const char * (*errmsg)(sqlite4*); - const void * (*errmsg16)(sqlite4*); - int (*exec)(sqlite4*,const char*,sqlite4_callback,void*,char**); - int (*expired)(sqlite4_stmt*); - int (*finalize)(sqlite4_stmt*pStmt); - void (*free)(void*); - void (*free_table)(char**result); - int (*get_autocommit)(sqlite4*); - void * (*get_auxdata)(sqlite4_context*,int); - int (*get_table)(sqlite4*,const char*,char***,int*,int*,char**); - int (*global_recover)(void); - void (*interruptx)(sqlite4*); - sqlite_int64 (*last_insert_rowid)(sqlite4*); - const char * (*libversion)(void); - int (*libversion_number)(void); - void *(*malloc)(int); - char * (*mprintf)(const char*,...); - int (*open)(const char*,sqlite4**); - int (*open16)(const void*,sqlite4**); - int (*prepare)(sqlite4*,const char*,int,sqlite4_stmt**,const char**); - int (*prepare16)(sqlite4*,const void*,int,sqlite4_stmt**,const void**); - void * (*profile)(sqlite4*,void(*)(void*,const char*,sqlite_uint64),void*); - void (*progress_handler)(sqlite4*,int,int(*)(void*),void*); - void *(*realloc)(void*,int); - int (*reset)(sqlite4_stmt*pStmt); - void (*result_blob)(sqlite4_context*,const void*,int,void(*)(void*)); - void (*result_double)(sqlite4_context*,double); - void (*result_error)(sqlite4_context*,const char*,int); - void (*result_error16)(sqlite4_context*,const void*,int); - void (*result_int)(sqlite4_context*,int); - void (*result_int64)(sqlite4_context*,sqlite_int64); - void (*result_null)(sqlite4_context*); - void (*result_text)(sqlite4_context*,const char*,int,void(*)(void*)); - void (*result_text16)(sqlite4_context*,const void*,int,void(*)(void*)); - void (*result_text16be)(sqlite4_context*,const void*,int,void(*)(void*)); - void (*result_text16le)(sqlite4_context*,const void*,int,void(*)(void*)); - void (*result_value)(sqlite4_context*,sqlite4_value*); - void * (*rollback_hook)(sqlite4*,void(*)(void*),void*); - int (*set_authorizer)(sqlite4*,int(*)(void*,int,const char*,const char*, - const char*,const char*),void*); - void (*set_auxdata)(sqlite4_context*,int,void*,void (*)(void*)); - char * (*snprintf)(int,char*,const char*,...); - int (*step)(sqlite4_stmt*); - int (*table_column_metadata)(sqlite4*,const char*,const char*,const char*, - char const**,char const**,int*,int*,int*); - void (*thread_cleanup)(void); - int (*total_changes)(sqlite4*); - void * (*trace)(sqlite4*,void(*xTrace)(void*,const char*),void*); - int (*transfer_bindings)(sqlite4_stmt*,sqlite4_stmt*); - void * (*update_hook)(sqlite4*,void(*)(void*,int ,char const*,char const*, - sqlite_int64),void*); - void * (*user_data)(sqlite4_context*); - const void * (*value_blob)(sqlite4_value*); - int (*value_bytes)(sqlite4_value*); - int (*value_bytes16)(sqlite4_value*); - double (*value_double)(sqlite4_value*); - int (*value_int)(sqlite4_value*); - sqlite_int64 (*value_int64)(sqlite4_value*); - int (*value_numeric_type)(sqlite4_value*); - const unsigned char * (*value_text)(sqlite4_value*); - const void * (*value_text16)(sqlite4_value*); - const void * (*value_text16be)(sqlite4_value*); - const void * (*value_text16le)(sqlite4_value*); - int (*value_type)(sqlite4_value*); - char *(*vmprintf)(const char*,va_list); - /* Added ??? */ - int (*overload_function)(sqlite4*, const char *zFuncName, int nArg); - /* Added by 3.3.13 */ - int (*prepare_v2)(sqlite4*,const char*,int,sqlite4_stmt**,const char**); - int (*prepare16_v2)(sqlite4*,const void*,int,sqlite4_stmt**,const void**); - int (*clear_bindings)(sqlite4_stmt*); - /* Added by 3.4.1 */ - int (*create_module_v2)(sqlite4*,const char*,const sqlite4_module*,void*, - void (*xDestroy)(void *)); - /* Added by 3.5.0 */ - int (*bind_zeroblob)(sqlite4_stmt*,int,int); - int (*blob_bytes)(sqlite4_blob*); - int (*blob_close)(sqlite4_blob*); - int (*blob_open)(sqlite4*,const char*,const char*,const char*,sqlite4_int64, - int,sqlite4_blob**); - int (*blob_read)(sqlite4_blob*,void*,int,int); - int (*blob_write)(sqlite4_blob*,const void*,int,int); - int (*file_control)(sqlite4*,const char*,int,void*); - sqlite4_int64 (*memory_highwater)(int); - sqlite4_int64 (*memory_used)(void); - sqlite4_mutex *(*mutex_alloc)(int); - void (*mutex_enter)(sqlite4_mutex*); - void (*mutex_free)(sqlite4_mutex*); - void (*mutex_leave)(sqlite4_mutex*); - int (*mutex_try)(sqlite4_mutex*); - int (*open_v2)(const char*,sqlite4**,int,const char*); - int (*release_memory)(int); - void (*result_error_nomem)(sqlite4_context*); - void (*result_error_toobig)(sqlite4_context*); - int (*sleep)(int); - void (*soft_heap_limit)(int); - sqlite4_vfs *(*vfs_find)(const char*); - int (*vfs_register)(sqlite4_vfs*,int); - int (*vfs_unregister)(sqlite4_vfs*); - int (*xthreadsafe)(void); - void (*result_zeroblob)(sqlite4_context*,int); - void (*result_error_code)(sqlite4_context*,int); - int (*test_control)(int, ...); - void (*randomness)(int,void*); - sqlite4 *(*context_db_handle)(sqlite4_context*); - int (*extended_result_codes)(sqlite4*,int); - int (*limit)(sqlite4*,int,int); - sqlite4_stmt *(*next_stmt)(sqlite4*,sqlite4_stmt*); - const char *(*sql)(sqlite4_stmt*); - int (*status)(int,int*,int*,int); - int (*backup_finish)(sqlite4_backup*); - sqlite4_backup *(*backup_init)(sqlite4*,const char*,sqlite4*,const char*); - int (*backup_pagecount)(sqlite4_backup*); - int (*backup_remaining)(sqlite4_backup*); - int (*backup_step)(sqlite4_backup*,int); - const char *(*compileoption_get)(int); - int (*compileoption_used)(const char*); - int (*create_function_v2)(sqlite4*,const char*,int,int,void*, - void (*xFunc)(sqlite4_context*,int,sqlite4_value**), - void (*xStep)(sqlite4_context*,int,sqlite4_value**), - void (*xFinal)(sqlite4_context*), - void(*xDestroy)(void*)); - int (*db_config)(sqlite4*,int,...); - sqlite4_mutex *(*db_mutex)(sqlite4*); - int (*db_status)(sqlite4*,int,int*,int*,int); - int (*extended_errcode)(sqlite4*); - void (*log)(int,const char*,...); - sqlite4_int64 (*soft_heap_limit64)(sqlite4_int64); - const char *(*sourceid)(void); - int (*stmt_status)(sqlite4_stmt*,int,int); - int (*strnicmp)(const char*,const char*,int); - int (*unlock_notify)(sqlite4*,void(*)(void**,int),void*); - int (*wal_autocheckpoint)(sqlite4*,int); - int (*wal_checkpoint)(sqlite4*,const char*); - void *(*wal_hook)(sqlite4*,int(*)(void*,sqlite4*,const char*,int),void*); - int (*blob_reopen)(sqlite4_blob*,sqlite4_int64); - int (*vtab_config)(sqlite4*,int op,...); - int (*vtab_on_conflict)(sqlite4*); -}; - -/* -** The following macros redefine the API routines so that they are -** redirected throught the global sqlite4_api structure. -** -** This header file is also used by the loadext.c source file -** (part of the main SQLite library - not an extension) so that -** it can get access to the sqlite4_api_routines structure -** definition. But the main library does not want to redefine -** the API. So the redefinition macros are only valid if the -** SQLITE_CORE macros is undefined. -*/ -#ifndef SQLITE_CORE -#define sqlite4_aggregate_context sqlite4_api->aggregate_context -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite4_aggregate_count sqlite4_api->aggregate_count -#endif -#define sqlite4_bind_blob sqlite4_api->bind_blob -#define sqlite4_bind_double sqlite4_api->bind_double -#define sqlite4_bind_int sqlite4_api->bind_int -#define sqlite4_bind_int64 sqlite4_api->bind_int64 -#define sqlite4_bind_null sqlite4_api->bind_null -#define sqlite4_bind_parameter_count sqlite4_api->bind_parameter_count -#define sqlite4_bind_parameter_index sqlite4_api->bind_parameter_index -#define sqlite4_bind_parameter_name sqlite4_api->bind_parameter_name -#define sqlite4_bind_text sqlite4_api->bind_text -#define sqlite4_bind_text16 sqlite4_api->bind_text16 -#define sqlite4_bind_value sqlite4_api->bind_value -#define sqlite4_busy_handler sqlite4_api->busy_handler -#define sqlite4_busy_timeout sqlite4_api->busy_timeout -#define sqlite4_changes sqlite4_api->changes -#define sqlite4_close sqlite4_api->close -#define sqlite4_collation_needed sqlite4_api->collation_needed -#define sqlite4_collation_needed16 sqlite4_api->collation_needed16 -#define sqlite4_column_blob sqlite4_api->column_blob -#define sqlite4_column_bytes sqlite4_api->column_bytes -#define sqlite4_column_bytes16 sqlite4_api->column_bytes16 -#define sqlite4_column_count sqlite4_api->column_count -#define sqlite4_column_database_name sqlite4_api->column_database_name -#define sqlite4_column_database_name16 sqlite4_api->column_database_name16 -#define sqlite4_column_decltype sqlite4_api->column_decltype -#define sqlite4_column_decltype16 sqlite4_api->column_decltype16 -#define sqlite4_column_double sqlite4_api->column_double -#define sqlite4_column_int sqlite4_api->column_int -#define sqlite4_column_int64 sqlite4_api->column_int64 -#define sqlite4_column_name sqlite4_api->column_name -#define sqlite4_column_name16 sqlite4_api->column_name16 -#define sqlite4_column_origin_name sqlite4_api->column_origin_name -#define sqlite4_column_origin_name16 sqlite4_api->column_origin_name16 -#define sqlite4_column_table_name sqlite4_api->column_table_name -#define sqlite4_column_table_name16 sqlite4_api->column_table_name16 -#define sqlite4_column_text sqlite4_api->column_text -#define sqlite4_column_text16 sqlite4_api->column_text16 -#define sqlite4_column_type sqlite4_api->column_type -#define sqlite4_column_value sqlite4_api->column_value -#define sqlite4_commit_hook sqlite4_api->commit_hook -#define sqlite4_complete sqlite4_api->complete -#define sqlite4_complete16 sqlite4_api->complete16 -#define sqlite4_create_collation sqlite4_api->create_collation -#define sqlite4_create_function sqlite4_api->create_function -#define sqlite4_create_function16 sqlite4_api->create_function16 -#define sqlite4_create_module sqlite4_api->create_module -#define sqlite4_create_module_v2 sqlite4_api->create_module_v2 -#define sqlite4_data_count sqlite4_api->data_count -#define sqlite4_db_handle sqlite4_api->db_handle -#define sqlite4_declare_vtab sqlite4_api->declare_vtab -#define sqlite4_enable_shared_cache sqlite4_api->enable_shared_cache -#define sqlite4_errcode sqlite4_api->errcode -#define sqlite4_errmsg sqlite4_api->errmsg -#define sqlite4_errmsg16 sqlite4_api->errmsg16 -#define sqlite4_exec sqlite4_api->exec -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite4_expired sqlite4_api->expired -#endif -#define sqlite4_finalize sqlite4_api->finalize -#define sqlite4_free sqlite4_api->free -#define sqlite4_free_table sqlite4_api->free_table -#define sqlite4_get_autocommit sqlite4_api->get_autocommit -#define sqlite4_get_auxdata sqlite4_api->get_auxdata -#define sqlite4_get_table sqlite4_api->get_table -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite4_global_recover sqlite4_api->global_recover -#endif -#define sqlite4_interrupt sqlite4_api->interruptx -#define sqlite4_last_insert_rowid sqlite4_api->last_insert_rowid -#define sqlite4_libversion sqlite4_api->libversion -#define sqlite4_libversion_number sqlite4_api->libversion_number -#define sqlite4_malloc sqlite4_api->malloc -#define sqlite4_mprintf sqlite4_api->mprintf -#define sqlite4_open sqlite4_api->open -#define sqlite4_open16 sqlite4_api->open16 -#define sqlite4_prepare sqlite4_api->prepare -#define sqlite4_prepare16 sqlite4_api->prepare16 -#define sqlite4_prepare_v2 sqlite4_api->prepare_v2 -#define sqlite4_prepare16_v2 sqlite4_api->prepare16_v2 -#define sqlite4_profile sqlite4_api->profile -#define sqlite4_progress_handler sqlite4_api->progress_handler -#define sqlite4_realloc sqlite4_api->realloc -#define sqlite4_reset sqlite4_api->reset -#define sqlite4_result_blob sqlite4_api->result_blob -#define sqlite4_result_double sqlite4_api->result_double -#define sqlite4_result_error sqlite4_api->result_error -#define sqlite4_result_error16 sqlite4_api->result_error16 -#define sqlite4_result_int sqlite4_api->result_int -#define sqlite4_result_int64 sqlite4_api->result_int64 -#define sqlite4_result_null sqlite4_api->result_null -#define sqlite4_result_text sqlite4_api->result_text -#define sqlite4_result_text16 sqlite4_api->result_text16 -#define sqlite4_result_text16be sqlite4_api->result_text16be -#define sqlite4_result_text16le sqlite4_api->result_text16le -#define sqlite4_result_value sqlite4_api->result_value -#define sqlite4_rollback_hook sqlite4_api->rollback_hook -#define sqlite4_set_authorizer sqlite4_api->set_authorizer -#define sqlite4_set_auxdata sqlite4_api->set_auxdata -#define sqlite4_snprintf sqlite4_api->snprintf -#define sqlite4_step sqlite4_api->step -#define sqlite4_table_column_metadata sqlite4_api->table_column_metadata -#define sqlite4_thread_cleanup sqlite4_api->thread_cleanup -#define sqlite4_total_changes sqlite4_api->total_changes -#define sqlite4_trace sqlite4_api->trace -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite4_transfer_bindings sqlite4_api->transfer_bindings -#endif -#define sqlite4_update_hook sqlite4_api->update_hook -#define sqlite4_user_data sqlite4_api->user_data -#define sqlite4_value_blob sqlite4_api->value_blob -#define sqlite4_value_bytes sqlite4_api->value_bytes -#define sqlite4_value_bytes16 sqlite4_api->value_bytes16 -#define sqlite4_value_double sqlite4_api->value_double -#define sqlite4_value_int sqlite4_api->value_int -#define sqlite4_value_int64 sqlite4_api->value_int64 -#define sqlite4_value_numeric_type sqlite4_api->value_numeric_type -#define sqlite4_value_text sqlite4_api->value_text -#define sqlite4_value_text16 sqlite4_api->value_text16 -#define sqlite4_value_text16be sqlite4_api->value_text16be -#define sqlite4_value_text16le sqlite4_api->value_text16le -#define sqlite4_value_type sqlite4_api->value_type -#define sqlite4_vmprintf sqlite4_api->vmprintf -#define sqlite4_overload_function sqlite4_api->overload_function -#define sqlite4_prepare_v2 sqlite4_api->prepare_v2 -#define sqlite4_prepare16_v2 sqlite4_api->prepare16_v2 -#define sqlite4_clear_bindings sqlite4_api->clear_bindings -#define sqlite4_bind_zeroblob sqlite4_api->bind_zeroblob -#define sqlite4_blob_bytes sqlite4_api->blob_bytes -#define sqlite4_blob_close sqlite4_api->blob_close -#define sqlite4_blob_open sqlite4_api->blob_open -#define sqlite4_blob_read sqlite4_api->blob_read -#define sqlite4_blob_write sqlite4_api->blob_write -#define sqlite4_file_control sqlite4_api->file_control -#define sqlite4_memory_highwater sqlite4_api->memory_highwater -#define sqlite4_memory_used sqlite4_api->memory_used -#define sqlite4_mutex_alloc sqlite4_api->mutex_alloc -#define sqlite4_mutex_enter sqlite4_api->mutex_enter -#define sqlite4_mutex_free sqlite4_api->mutex_free -#define sqlite4_mutex_leave sqlite4_api->mutex_leave -#define sqlite4_mutex_try sqlite4_api->mutex_try -#define sqlite4_open_v2 sqlite4_api->open_v2 -#define sqlite4_release_memory sqlite4_api->release_memory -#define sqlite4_result_error_nomem sqlite4_api->result_error_nomem -#define sqlite4_result_error_toobig sqlite4_api->result_error_toobig -#define sqlite4_sleep sqlite4_api->sleep -#define sqlite4_soft_heap_limit sqlite4_api->soft_heap_limit -#define sqlite4_vfs_find sqlite4_api->vfs_find -#define sqlite4_vfs_register sqlite4_api->vfs_register -#define sqlite4_vfs_unregister sqlite4_api->vfs_unregister -#define sqlite4_threadsafe sqlite4_api->xthreadsafe -#define sqlite4_result_zeroblob sqlite4_api->result_zeroblob -#define sqlite4_result_error_code sqlite4_api->result_error_code -#define sqlite4_test_control sqlite4_api->test_control -#define sqlite4_randomness sqlite4_api->randomness -#define sqlite4_context_db_handle sqlite4_api->context_db_handle -#define sqlite4_extended_result_codes sqlite4_api->extended_result_codes -#define sqlite4_limit sqlite4_api->limit -#define sqlite4_next_stmt sqlite4_api->next_stmt -#define sqlite4_sql sqlite4_api->sql -#define sqlite4_status sqlite4_api->status -#define sqlite4_backup_finish sqlite4_api->backup_finish -#define sqlite4_backup_init sqlite4_api->backup_init -#define sqlite4_backup_pagecount sqlite4_api->backup_pagecount -#define sqlite4_backup_remaining sqlite4_api->backup_remaining -#define sqlite4_backup_step sqlite4_api->backup_step -#define sqlite4_compileoption_get sqlite4_api->compileoption_get -#define sqlite4_compileoption_used sqlite4_api->compileoption_used -#define sqlite4_create_function_v2 sqlite4_api->create_function_v2 -#define sqlite4_db_config sqlite4_api->db_config -#define sqlite4_db_mutex sqlite4_api->db_mutex -#define sqlite4_db_status sqlite4_api->db_status -#define sqlite4_extended_errcode sqlite4_api->extended_errcode -#define sqlite4_log sqlite4_api->log -#define sqlite4_soft_heap_limit64 sqlite4_api->soft_heap_limit64 -#define sqlite4_sourceid sqlite4_api->sourceid -#define sqlite4_stmt_status sqlite4_api->stmt_status -#define sqlite4_strnicmp sqlite4_api->strnicmp -#define sqlite4_unlock_notify sqlite4_api->unlock_notify -#define sqlite4_wal_autocheckpoint sqlite4_api->wal_autocheckpoint -#define sqlite4_wal_checkpoint sqlite4_api->wal_checkpoint -#define sqlite4_wal_hook sqlite4_api->wal_hook -#define sqlite4_blob_reopen sqlite4_api->blob_reopen -#define sqlite4_vtab_config sqlite4_api->vtab_config -#define sqlite4_vtab_on_conflict sqlite4_api->vtab_on_conflict -#endif /* SQLITE_CORE */ - -#define SQLITE_EXTENSION_INIT1 const sqlite4_api_routines *sqlite4_api = 0; -#define SQLITE_EXTENSION_INIT2(v) sqlite4_api = v; - -#endif /* _SQLITE3EXT_H_ */ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -677,18 +677,15 @@ typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; typedef struct WhereLevel WhereLevel; /* -** Defer sourcing vdbe.h and btree.h until after the "u8" and +** Defer sourcing vdbe.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ -#include "btree.h" #include "vdbe.h" -#include "pager.h" -#include "pcache.h" #include "storage.h" #include "os.h" #include "mutex.h" @@ -700,32 +697,29 @@ ** aDb[1] is the database file used to hold temporary tables. Additional ** databases may be attached. */ struct Db { char *zName; /* Name of this database */ - Btree *pBt; /* The B*Tree structure for this database file */ KVStore *pKV; /* KV store for the database file */ u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ - u8 safety_level; /* How aggressive at syncing data to disk */ + u8 chngFlag; /* True if modified */ Schema *pSchema; /* Pointer to database schema (possibly shared) */ }; /* ** An instance of the following structure stores a database schema. ** -** Most Schema objects are associated with a Btree. The exception is +** Most Schema objects are associated with a database file. The exception is ** the Schema for the TEMP databaes (sqlite4.aDb[1]) which is free-standing. -** In shared cache mode, a single Schema object can be shared by multiple -** Btrees that refer to the same underlying BtShared object. ** -** Schema objects are automatically deallocated when the last Btree that +** Schema objects are automatically deallocated when the last database that ** references them is destroyed. The TEMP Schema is manually freed by ** sqlite4_close(). * -** A thread must be holding a mutex on the corresponding Btree in order +** A thread must be holding a mutex on the corresponding database in order ** to access Schema content. This implies that the thread must also be -** holding a mutex on the sqlite4 connection pointer that owns the Btree. +** holding a mutex on the sqlite4 connection pointer that owns the database ** For a TEMP Schema, only the connection mutex is required. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ int iGeneration; /* Generation counter. Incremented with each change */ @@ -957,33 +951,26 @@ /* ** Possible values for the sqlite4.flags. */ #define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */ #define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */ -#define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */ -#define SQLITE_ShortColNames 0x00000800 /* Show short columns names */ #define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ -#define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */ - /* result set is empty */ #define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */ #define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */ #define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ - /* 0x00020000 Unused */ +#define SQLITE_KvTrace 0x00020000 /* Trace Key/value storage calls */ #define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ #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_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 */ #define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */ /* ** Bits of the sqlite4.flags field that are used by the ** sqlite4_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. @@ -1434,18 +1421,19 @@ #define OE_Default 99 /* Do whatever the default action is */ /* -** An instance of the following structure is passed as the first -** argument to sqlite4VdbeKeyCompare and is used to control the -** comparison of the two index keys. +** An instance of the following structure describes an index key. It +** includes information such as sort order and collating sequence for +** each key, and the number of primary key fields appended to the end. */ struct KeyInfo { sqlite4 *db; /* The database connection */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ - u16 nField; /* Number of entries in aColl[] */ + u16 nField; /* Total number of entries in aColl[] */ + u16 nPK; /* Number of primary key entries at the end of aColl[] */ u8 *aSortOrder; /* Sort order for each column. May be NULL */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* @@ -2326,11 +2314,11 @@ */ #define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ #define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ -#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ +#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek on insert */ #define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ #define OPFLAG_APPENDBIAS 0x40 /* Bias inserts for appending */ /* * Each trigger present in the database schema is stored as an instance of @@ -2968,11 +2956,11 @@ int sqlite4PutVarint(unsigned char*, u64); int sqlite4PutVarint32(unsigned char*, u32); u8 sqlite4GetVarint(const unsigned char *, u64 *); u8 sqlite4GetVarint32(const unsigned char *, u32 *); int sqlite4VarintLen(u64 v); -int sqlite4GetVarint64(const unsigned char*, sqlite4_uint64 *pResult); +int sqlite4GetVarint64(const unsigned char*, int, sqlite4_uint64 *pResult); int sqlite4PutVarint64(unsigned char*, sqlite4_uint64); /* ** The header of a record consists of a sequence variable-length integers. ** These integers are almost always small and are encoded as a single byte. @@ -3074,13 +3062,12 @@ int sqlite4AnalysisLoad(sqlite4*,int iDB); void sqlite4DeleteIndexSamples(sqlite4*,Index*); void sqlite4DefaultRowEst(Index*); void sqlite4RegisterLikeFunctions(sqlite4*, int); int sqlite4IsLikeFunction(sqlite4*,Expr*,int*,char*); -void sqlite4MinimumFileFormat(Parse*, int, int); -void sqlite4SchemaClear(void *); -Schema *sqlite4SchemaGet(sqlite4 *, Btree *); +void sqlite4SchemaClear(Schema*); +Schema *sqlite4SchemaGet(sqlite4*); int sqlite4SchemaToIndex(sqlite4 *db, Schema *); KeyInfo *sqlite4IndexKeyinfo(Parse *, Index *); int sqlite4CreateFunc(sqlite4 *, const char *, int, int, void *, void (*)(sqlite4_context*,int,sqlite4_value **), void (*)(sqlite4_context*,int,sqlite4_value **), void (*)(sqlite4_context*), @@ -3095,13 +3082,10 @@ char *sqlite4StrAccumFinish(StrAccum*); void sqlite4StrAccumReset(StrAccum*); void sqlite4SelectDestInit(SelectDest*,int,int); Expr *sqlite4CreateColumnExpr(sqlite4 *, SrcList *, int, int); -void sqlite4BackupRestart(sqlite4_backup *); -void sqlite4BackupUpdate(sqlite4_backup *, Pgno, const u8 *); - /* ** The interface to the LEMON-generated parser */ void *sqlite4ParserAlloc(void*(*)(size_t)); void sqlite4ParserFree(void*, void(*)(void*)); Index: src/status.c ================================================================== --- src/status.c +++ src/status.c @@ -138,19 +138,10 @@ ** highwater mark is meaningless and is returned as zero. */ case SQLITE_DBSTATUS_CACHE_USED: { int totalUsed = 0; int i; - sqlite4BtreeEnterAll(db); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - Pager *pPager = sqlite4BtreePager(pBt); - totalUsed += sqlite4PagerMemUsed(pPager); - } - } - sqlite4BtreeLeaveAll(db); *pCurrent = totalUsed; *pHighwater = 0; break; } @@ -161,11 +152,10 @@ */ case SQLITE_DBSTATUS_SCHEMA_USED: { int i; /* Used to iterate through schemas */ int nByte = 0; /* Used to accumulate return value */ - sqlite4BtreeEnterAll(db); db->pnBytesFreed = &nByte; for(i=0; inDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( ALWAYS(pSchema!=0) ){ HashElem *p; @@ -188,11 +178,10 @@ sqlite4DeleteTable(db, (Table *)sqliteHashData(p)); } } } db->pnBytesFreed = 0; - sqlite4BtreeLeaveAll(db); *pHighwater = 0; *pCurrent = nByte; break; } @@ -225,18 +214,10 @@ */ case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: { int i; int nRet = 0; - assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); - - for(i=0; inDb; i++){ - if( db->aDb[i].pBt ){ - Pager *pPager = sqlite4BtreePager(db->aDb[i].pBt); - sqlite4PagerCacheStat(pPager, op, resetFlag, &nRet); - } - } *pHighwater = 0; *pCurrent = nRet; break; } Index: src/storage.c ================================================================== --- src/storage.c +++ src/storage.c @@ -9,29 +9,95 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** General wrapper functions around the various KV storage engine -** implementations. +** implementations. It also implements tracing of calls to the KV +** engine and some higher-level ensembles of the low-level storage +** calls. */ #include "sqliteInt.h" + +/* +** Names of error codes used for tracing. +*/ +static const char *kvErrName(int e){ + const char *zName; + switch( e ){ + case SQLITE_OK: zName = "OK"; break; + case SQLITE_ERROR: zName = "ERROR"; break; + case SQLITE_INTERNAL: zName = "INTERNAL"; break; + case SQLITE_PERM: zName = "PERM"; break; + case SQLITE_ABORT: zName = "ABORT"; break; + case SQLITE_BUSY: zName = "BUSY"; break; + case SQLITE_LOCKED: zName = "LOCKED"; break; + case SQLITE_NOMEM: zName = "NOMEM"; break; + case SQLITE_READONLY: zName = "READONLY"; break; + case SQLITE_INTERRUPT: zName = "INTERRUPT"; break; + case SQLITE_IOERR: zName = "IOERR"; break; + case SQLITE_CORRUPT: zName = "CORRUPT"; break; + case SQLITE_NOTFOUND: zName = "NOTFOUND"; break; + case SQLITE_FULL: zName = "FULL"; break; + case SQLITE_CANTOPEN: zName = "CANTOPEN"; break; + case SQLITE_PROTOCOL: zName = "PROTOCOL"; break; + case SQLITE_EMPTY: zName = "EMPTY"; break; + case SQLITE_SCHEMA: zName = "SCHEMA"; break; + case SQLITE_TOOBIG: zName = "TOOBIG"; break; + case SQLITE_CONSTRAINT: zName = "CONSTRAINT"; break; + case SQLITE_MISMATCH: zName = "MISMATCH"; break; + case SQLITE_MISUSE: zName = "MISUSE"; break; + case SQLITE_NOLFS: zName = "NOLFS"; break; + case SQLITE_AUTH: zName = "AUTH"; break; + case SQLITE_FORMAT: zName = "FORMAT"; break; + case SQLITE_RANGE: zName = "RANGE"; break; + case SQLITE_NOTADB: zName = "NOTADB"; break; + case SQLITE_ROW: zName = "ROW"; break; + case SQLITE_DONE: zName = "DONE"; break; + case SQLITE_INEXACT: zName = "INEXACT"; break; + default: zName = "???"; break; + } + return zName; +} + +/* +** Do any requested tracing +*/ +static void kvTrace(KVStore *p, const char *zFormat, ...){ + if( p->fTrace ){ + va_list ap; + char *z; + + va_start(ap, zFormat); + z = sqlite4_vmprintf(zFormat, ap); + va_end(ap); + printf("%s.%s\n", p->zKVName, z); + fflush(stdout); + sqlite4_free(z); + } +} /* ** Open a storage engine via URI */ -int sqlite4KVStoreOpen(const char *zUri, KVStore **ppKVStore){ +int sqlite4KVStoreOpen( + sqlite4 *db, /* The database connection doing the open */ + const char *zName, /* Symbolic name for this database */ + const char *zUri, /* URI for this database */ + KVStore **ppKVStore, /* Write the new KVStore object here */ + unsigned flags /* Option flags */ +){ KVStore *pNew = 0; int rc; - rc = sqlite4KVStoreOpenMem(&pNew); + rc = sqlite4KVStoreOpenMem(&pNew, flags); *ppKVStore = pNew; if( pNew ){ sqlite4_randomness(sizeof(pNew->kvId), &pNew->kvId); - pNew->fTrace = 0; - if( pNew->fTrace ){ - printf("KVopen(%s,%d)\n", zUri, pNew->kvId); - } + sqlite4_snprintf(sizeof(pNew->zKVName), pNew->zKVName, + "%s", zName); + pNew->fTrace = (db->flags & SQLITE_KvTrace)!=0; + kvTrace(pNew, "open(%s,%d,0x%04x)", zUri, pNew->kvId, flags); } return rc; } /* Convert binary data to hex for display in trace messages */ @@ -55,12 +121,12 @@ const KVByteArray *pData, KVSize nData ){ if( p->fTrace ){ char zKey[52], zData[52]; binToHex(zKey, sizeof(zKey), pKey, nKey); - binToHex(zData, sizeof(zKey), pData, nData); - printf("KV.xReplace(%d,%s,%d,%s,%d)\n", + binToHex(zData, sizeof(zData), pData, nData); + kvTrace(p, "xReplace(%d,%s,%d,%s,%d)", p->kvId, zKey, (int)nKey, zData, (int)nData); } return p->pStoreVfunc->xReplace(p,pKey,nKey,pData,nData); } int sqlite4KVStoreOpenCursor(KVStore *p, KVCursor **ppKVCursor){ @@ -70,14 +136,14 @@ rc = p->pStoreVfunc->xOpenCursor(p, &pCur); *ppKVCursor = pCur; if( pCur ){ sqlite4_randomness(sizeof(pCur->curId), &pCur->curId); pCur->fTrace = p->fTrace; + pCur->pStore = p; } - if( p->fTrace ){ - printf("KV.xOpenCursor(%d,%d) -> %d\n", p->kvId, pCur?pCur->curId:-1, rc); - } + kvTrace(p, "xOpenCursor(%d,%d) -> %s", + p->kvId, pCur?pCur->curId:-1, kvErrName(rc)); return rc; } int sqlite4KVCursorSeek( KVCursor *p, const KVByteArray *pKey, KVSize nKey, @@ -86,56 +152,49 @@ int rc; rc = p->pStoreVfunc->xSeek(p,pKey,nKey,dir); if( p->fTrace ){ char zKey[52]; binToHex(zKey, sizeof(zKey), pKey, nKey); - printf("KV.xSeek(%d,%s,%d,%d) -> %d\n", p->curId, zKey, (int)nKey,dir,rc); + kvTrace(p->pStore, "xSeek(%d,%s,%d,%d) -> %s", + p->curId, zKey, (int)nKey, dir, kvErrName(rc)); } return rc; } int sqlite4KVCursorNext(KVCursor *p){ int rc; rc = p->pStoreVfunc->xNext(p); - if( p->fTrace ){ - printf("KV.xNext(%d) -> %d\n", p->curId, rc); - } + kvTrace(p->pStore, "xNext(%d) -> %s", p->curId, kvErrName(rc)); return rc; } int sqlite4KVCursorPrev(KVCursor *p){ int rc; rc = p->pStoreVfunc->xPrev(p); - if( p->fTrace ){ - printf("KV.xPrev(%d) -> %d\n", p->curId, rc); - } + kvTrace(p->pStore, "xPrev(%d) -> %s", p->curId, kvErrName(rc)); return rc; } int sqlite4KVCursorDelete(KVCursor *p){ int rc; rc = p->pStoreVfunc->xDelete(p); - if( p->fTrace ){ - printf("KV.xDelete(%d) -> %d\n", p->curId, rc); - } + kvTrace(p->pStore, "xDelete(%d) -> %s", p->curId, kvErrName(rc)); return rc; } int sqlite4KVCursorReset(KVCursor *p){ int rc; rc = p->pStoreVfunc->xReset(p); - if( p->fTrace ){ - printf("KV.xReset(%d) -> %d\n", p->curId, rc); - } + kvTrace(p->pStore, "xReset(%d) -> %s", p->curId, kvErrName(rc)); return rc; } int sqlite4KVCursorKey(KVCursor *p, const KVByteArray **ppKey, KVSize *pnKey){ int rc; rc = p->pStoreVfunc->xKey(p, ppKey, pnKey); if( p->fTrace ){ if( rc==SQLITE_OK ){ char zKey[52]; binToHex(zKey, sizeof(zKey), *ppKey, *pnKey); - printf("KV.xKey(%d,%s,%d)\n", p->curId, zKey, (int)*pnKey); + kvTrace(p->pStore, "xKey(%d,%s,%d)", p->curId, zKey, (int)*pnKey); }else{ - printf("KV.xKey(%d,)\n", p->curId, rc); + kvTrace(p->pStore, "xKey(%d,)", p->curId, rc); } } return rc; } int sqlite4KVCursorData( @@ -149,55 +208,191 @@ rc = p->pStoreVfunc->xData(p, ofst, n, ppData, pnData); if( p->fTrace ){ if( rc==SQLITE_OK ){ char zData[52]; binToHex(zData, sizeof(zData), *ppData, *pnData); - printf("KV.xData(%d,%d,%d,%s,%d)\n", + kvTrace(p->pStore, "xData(%d,%d,%d,%s,%d)", p->curId, (int)ofst, (int)n, zData, (int)*pnData); }else{ - printf("KV.xData(%d,%d,%d,)\n", + kvTrace(p->pStore, "xData(%d,%d,%d,)", p->curId, (int)ofst, (int)n, rc); } } return rc; } int sqlite4KVCursorClose(KVCursor *p){ int rc = SQLITE_OK; if( p ){ + KVStore *pStore = p->pStore; + int curId = p->curId; rc = p->pStoreVfunc->xCloseCursor(p); - if( p->fTrace ){ - printf("KV.xCloseCursor(%d) -> %d\n", p->curId, rc); - } + kvTrace(pStore, "xCloseCursor(%d) -> %s", curId, kvErrName(rc)); } return rc; } int sqlite4KVStoreBegin(KVStore *p, int iLevel){ - int rc = p->pStoreVfunc->xBegin(p, iLevel); - if( p->fTrace ){ - printf("KV.xBegin(%d,%d) -> %d\n", p->kvId, iLevel, rc); + int rc; + assert( (iLevel==2 && p->iTransLevel==0) || p->iTransLevel+1==iLevel ); + rc = p->pStoreVfunc->xBegin(p, iLevel); + kvTrace(p, "xBegin(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE_OK ); + return rc; +} +int sqlite4KVStoreCommitPhaseOne(KVStore *p, int iLevel){ + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + if( p->iTransLevel==iLevel ) return SQLITE_OK; + if( p->pStoreVfunc->xCommitPhaseOne ){ + rc = p->pStoreVfunc->xCommitPhaseOne(p, iLevel); + }else{ + rc = SQLITE_OK; } + kvTrace(p, "xCommitPhaseOne(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel>iLevel ); + return rc; +} +int sqlite4KVStoreCommitPhaseTwo(KVStore *p, int iLevel){ + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + if( p->iTransLevel==iLevel ) return SQLITE_OK; + rc = p->pStoreVfunc->xCommitPhaseTwo(p, iLevel); + kvTrace(p, "xCommitPhaseTwo(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE_OK ); return rc; } int sqlite4KVStoreCommit(KVStore *p, int iLevel){ - int rc = p->pStoreVfunc->xCommit(p, iLevel); - if( p->fTrace ){ - printf("KV.xCommit(%d,%d) -> %d\n", p->kvId, iLevel, rc); - } + int rc; + rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); + if( rc==SQLITE_OK ) rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); return rc; } int sqlite4KVStoreRollback(KVStore *p, int iLevel){ - int rc = p->pStoreVfunc->xRollback(p, iLevel); - if( p->fTrace ){ - printf("KV.xRollback(%d,%d) -> %d\n", p->kvId, iLevel, rc); + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + rc = p->pStoreVfunc->xRollback(p, iLevel); + kvTrace(p, "xRollback(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE_OK ); + return rc; +} +int sqlite4KVStoreRevert(KVStore *p, int iLevel){ + int rc; + assert( iLevel>0 ); + assert( iLevel<=p->iTransLevel ); + if( p->pStoreVfunc->xRevert ){ + rc = p->pStoreVfunc->xRevert(p, iLevel); + kvTrace(p, "xRevert(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + }else{ + rc = sqlite4KVStoreRollback(p, iLevel-1); + if( rc==SQLITE_OK ){ + rc = sqlite4KVStoreBegin(p, iLevel); + } } + assert( p->iTransLevel==iLevel || rc!=SQLITE_OK ); return rc; } int sqlite4KVStoreClose(KVStore *p){ int rc; if( p ){ - if( p->fTrace ){ - printf("KV.xClose(%d)\n", p->kvId); - } + kvTrace(p, "xClose(%d)", p->kvId); rc = p->pStoreVfunc->xClose(p); } return rc; } + +/* +** Key for the meta-data +*/ +static const KVByteArray metadataKey[] = { 0x00, 0x00 }; + +/* +** Read nMeta unsigned 32-bit integers of metadata beginning at iStart. +*/ +int sqlite4KVStoreGetMeta(KVStore *p, int iStart, int nMeta, unsigned int *a){ + KVCursor *pCur; + int rc; + int i, j; + KVSize nData; + const KVByteArray *aData; + + rc = sqlite4KVStoreOpenCursor(p, &pCur); + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorSeek(pCur, metadataKey, sizeof(metadataKey), 0); + if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; + nData = 0; + }else if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); + } + if( rc==SQLITE_OK ){ + i = 0; + j = iStart*4; + while( i>24)&0xff; + aNew[j+1] = (a[i]>>16)&0xff; + aNew[j+2] = (a[i]>>8)&0xff; + aNew[j+3] = a[i] & 0xff; + i++; + j += 4; + } + rc = sqlite4KVStoreReplace(p, metadataKey, sizeof(metadataKey), + aNew, nNew); + sqlite4DbFree(db, aNew); + } + } + sqlite4KVCursorClose(pCur); + } + return rc; +} Index: src/storage.h ================================================================== --- src/storage.h +++ src/storage.h @@ -22,30 +22,40 @@ ** of the store. The transaction level is a non-negative integer that is ** initialized to zero. The transaction level must be at least 1 in order ** for content to be read. The transaction level must be at least 2 for ** content to be modified. ** -** The xBegin method increases transaction level. The increase may only be -** by an amount of 1 unless the transaction level is initially 0 in which case +** The xBegin method increases transaction level. The increase may be no +** more than 1 unless the transaction level is initially 0 in which case ** it can be increased immediately to 2. Increasing the transaction level ** to 1 or more makes a "snapshot" of the complete store such that changes ** made by other connections are not visible. An xBegin call may fail ** with SQLITE_BUSY if the initial transaction level is 0 or 1. ** ** A read-only store will fail an attempt to increase xBegin above 1. An ** implementation that does not support nested transactions will fail any ** attempt to increase the transaction level above 2. ** -** The xCommit method lowers the transaction level to the value given in its -** argument, and makes all the changes made at higher transaction levels -** permanent. +** The xCommitPhaseOne and xCommitPhaseTwo methods implementat a 2-phase +** commit that lowers the transaction level to the value given in the +** second argument, and makes all the changes made at higher transaction levels +** permanent. A rollback is still possible following phase one. If +** possible, errors should be reported during phase one so that a +** multiple-database transaction can still be rolled back if the +** phase one fails on a different database. Implementations that do not +** support two-phase commit can implement xCommitPhaseOne as a no-op function +** returning SQLITE_OK. ** ** The xRollback method lowers the transaction level to the value given in ** its argument and reverts or undoes all changes made at higher transaction ** levels. An xRollback to level N causes the database to revert to the state ** it was in on the most recent xBegin to level N+1. ** +** The xRevert(N) method causes the state of the database file to go back +** to what it was immediately after the most recent xCommit(N). Higher-level +** subtransactions are cancelled. This call is equivalent to xRollback(N-1) +** followed by xBegin(N) but might be more efficient. ** ** The xReplace method replaces the value for an existing entry with the ** given key, or creates a new entry with the given key and value if no ** prior entry exists with the given key. The key and value pointers passed ** into xReplace will likely be destroyed when the call to xReplace returns @@ -66,10 +76,24 @@ ** entry that is less than the search key or to EOF if the store contains no ** entry less than the search key. If dir is positive, then an exist match ** is found if it is available, otherwise the cursor is left pointing the ** the smallest entry that is larger than the search key, or to EOF if there ** are no entries larger than the search key. +** +** The xSeek return code might be one of the following: +** +** SQLITE_OK The cursor is left pointing to any entry that +** exactly matchings the probe key. +** +** SQLITE_INEXACT The cursor is left pointing to the nearest entry +** to the probe it could find, either before or after +** the probe, according to the dir argument. +** +** SQLITE_NOTFOUND No suitable entry could be found. Either dir==0 and +** there was no exact match, or dir<0 and the probe is +** smaller than every entry in the database, or dir>0 and +** the probe is larger than every entry in the database. ** ** The xNext method may only be used following an xSeek with a positive dir, ** or another xNext. The xPrev method may only be used following an xSeek with ** a negative dir or another xPrev. ** @@ -94,11 +118,11 @@ typedef unsigned char KVByteArray; typedef int KVSize; /* ** A Key-Value storage engine is defined by an instance of the following -** structure. +** structures: */ struct KVStoreMethods { int (*xReplace)(KVStore*, const KVByteArray *pKey, KVSize nKey, const KVByteArray *pData, KVSize nData); int (*xOpenCursor)(KVStore*, KVCursor**); @@ -110,35 +134,51 @@ int (*xData)(KVCursor*, KVSize ofst, KVSize n, const KVByteArray **ppData, KVSize *pnData); int (*xReset)(KVCursor*); int (*xCloseCursor)(KVCursor*); int (*xBegin)(KVStore*, int); - int (*xCommit)(KVStore*, int); + int (*xCommitPhaseOne)(KVStore*, int); + int (*xCommitPhaseTwo)(KVStore*, int); int (*xRollback)(KVStore*, int); + int (*xRevert)(KVStore*, int); int (*xClose)(KVStore*); }; struct KVStore { const KVStoreMethods *pStoreVfunc; /* Virtual method table */ + int iTransLevel; /* Current transaction level */ u16 kvId; /* Unique ID used for tracing */ u8 fTrace; /* True to enable tracing */ + char zKVName[12]; /* Used for debugging */ /* Subclasses will typically append additional fields */ }; /* ** Base class for cursors */ struct KVCursor { KVStore *pStore; /* The owner of this cursor */ const KVStoreMethods *pStoreVfunc; /* Methods */ + int iTransLevel; /* Current transaction level */ u16 curId; /* Unique ID for tracing */ u8 fTrace; /* True to enable tracing */ /* Subclasses will typically add additional fields */ }; +/* +** Valid flags for sqlite4KVStorageOpen() +*/ +#define SQLITE_KVOPEN_TEMPORARY 0x0001 /* A temporary database */ +#define SQLITE_KVOPEN_NO_TRANSACTIONS 0x0002 /* No transactions will be used */ -int sqlite4KVStoreOpenMem(KVStore**); -int sqlite4KVStoreOpen(const char *zUri, KVStore**); +int sqlite4KVStoreOpenMem(KVStore**, unsigned); +int sqlite4KVStoreOpen( + sqlite4*, + const char *zLabel, + const char *zUri, + KVStore**, + unsigned flags +); int sqlite4KVStoreReplace( KVStore*, const KVByteArray *pKey, KVSize nKey, const KVByteArray *pData, KVSize nData ); @@ -160,8 +200,14 @@ const KVByteArray **ppData, KVSize *pnData ); int sqlite4KVCursorClose(KVCursor *p); int sqlite4KVStoreBegin(KVStore *p, int iLevel); +int sqlite4KVStoreCommitPhaseOne(KVStore *p, int iLevel); +int sqlite4KVStoreCommitPhaseTwo(KVStore *p, int iLevel); int sqlite4KVStoreCommit(KVStore *p, int iLevel); int sqlite4KVStoreRollback(KVStore *p, int iLevel); +int sqlite4KVStoreRevert(KVStore *p, int iLevel); int sqlite4KVStoreClose(KVStore *p); + +int sqlite4KVStoreGetMeta(KVStore *p, int, int, unsigned int*); +int sqlite4KVStorePutMeta(sqlite4*, KVStore *p, int, int, unsigned int*); Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -100,12 +100,10 @@ const char *zSql; /* Text of the SQL statement */ int nParm; /* Size of apParm array */ Tcl_Obj **apParm; /* Array of referenced object pointers */ }; -typedef struct IncrblobChannel IncrblobChannel; - /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. ** ** If this module is built with SQLITE_TEST defined (to create the SQLite @@ -135,27 +133,17 @@ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ - IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ int nStep, nSort, nIndex; /* Statistics for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite4_prepare() */ #endif }; -struct IncrblobChannel { - sqlite4_blob *pBlob; /* sqlite4 blob handle */ - SqliteDb *pDb; /* Associated database connection */ - int iSeek; /* Current seek offset */ - Tcl_Channel channel; /* Channel identifier */ - IncrblobChannel *pNext; /* Linked list of all open incrblob channels */ - IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */ -}; - /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ @@ -163,229 +151,10 @@ while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Close all incrblob channels opened using database connection pDb. -** This is called when shutting down the database connection. -*/ -static void closeIncrblobChannels(SqliteDb *pDb){ - IncrblobChannel *p; - IncrblobChannel *pNext; - - for(p=pDb->pIncrblob; p; p=pNext){ - pNext = p->pNext; - - /* Note: Calling unregister here call Tcl_Close on the incrblob channel, - ** which deletes the IncrblobChannel structure at *p. So do not - ** call Tcl_Free() here. - */ - Tcl_UnregisterChannel(pDb->interp, p->channel); - } -} - -/* -** Close an incremental blob channel. -*/ -static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ - IncrblobChannel *p = (IncrblobChannel *)instanceData; - int rc = sqlite4_blob_close(p->pBlob); - sqlite4 *db = p->pDb->db; - - /* Remove the channel from the SqliteDb.pIncrblob list. */ - if( p->pNext ){ - p->pNext->pPrev = p->pPrev; - } - if( p->pPrev ){ - p->pPrev->pNext = p->pNext; - } - if( p->pDb->pIncrblob==p ){ - p->pDb->pIncrblob = p->pNext; - } - - /* Free the IncrblobChannel structure */ - Tcl_Free((char *)p); - - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite4_errmsg(db), TCL_VOLATILE); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Read data from an incremental blob channel. -*/ -static int incrblobInput( - ClientData instanceData, - char *buf, - int bufSize, - int *errorCodePtr -){ - IncrblobChannel *p = (IncrblobChannel *)instanceData; - int nRead = bufSize; /* Number of bytes to read */ - int nBlob; /* Total size of the blob */ - int rc; /* sqlite error code */ - - nBlob = sqlite4_blob_bytes(p->pBlob); - if( (p->iSeek+nRead)>nBlob ){ - nRead = nBlob-p->iSeek; - } - if( nRead<=0 ){ - return 0; - } - - rc = sqlite4_blob_read(p->pBlob, (void *)buf, nRead, p->iSeek); - if( rc!=SQLITE_OK ){ - *errorCodePtr = rc; - return -1; - } - - p->iSeek += nRead; - return nRead; -} - -/* -** Write data to an incremental blob channel. -*/ -static int incrblobOutput( - ClientData instanceData, - CONST char *buf, - int toWrite, - int *errorCodePtr -){ - IncrblobChannel *p = (IncrblobChannel *)instanceData; - int nWrite = toWrite; /* Number of bytes to write */ - int nBlob; /* Total size of the blob */ - int rc; /* sqlite error code */ - - nBlob = sqlite4_blob_bytes(p->pBlob); - if( (p->iSeek+nWrite)>nBlob ){ - *errorCodePtr = EINVAL; - return -1; - } - if( nWrite<=0 ){ - return 0; - } - - rc = sqlite4_blob_write(p->pBlob, (void *)buf, nWrite, p->iSeek); - if( rc!=SQLITE_OK ){ - *errorCodePtr = EIO; - return -1; - } - - p->iSeek += nWrite; - return nWrite; -} - -/* -** Seek an incremental blob channel. -*/ -static int incrblobSeek( - ClientData instanceData, - long offset, - int seekMode, - int *errorCodePtr -){ - IncrblobChannel *p = (IncrblobChannel *)instanceData; - - switch( seekMode ){ - case SEEK_SET: - p->iSeek = offset; - break; - case SEEK_CUR: - p->iSeek += offset; - break; - case SEEK_END: - p->iSeek = sqlite4_blob_bytes(p->pBlob) + offset; - break; - - default: assert(!"Bad seekMode"); - } - - return p->iSeek; -} - - -static void incrblobWatch(ClientData instanceData, int mode){ - /* NO-OP */ -} -static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){ - return TCL_ERROR; -} - -static Tcl_ChannelType IncrblobChannelType = { - "incrblob", /* typeName */ - TCL_CHANNEL_VERSION_2, /* version */ - incrblobClose, /* closeProc */ - incrblobInput, /* inputProc */ - incrblobOutput, /* outputProc */ - incrblobSeek, /* seekProc */ - 0, /* setOptionProc */ - 0, /* getOptionProc */ - incrblobWatch, /* watchProc (this is a no-op) */ - incrblobHandle, /* getHandleProc (always returns error) */ - 0, /* close2Proc */ - 0, /* blockModeProc */ - 0, /* flushProc */ - 0, /* handlerProc */ - 0, /* wideSeekProc */ -}; - -/* -** Create a new incrblob channel. -*/ -static int createIncrblobChannel( - Tcl_Interp *interp, - SqliteDb *pDb, - const char *zDb, - const char *zTable, - const char *zColumn, - sqlite_int64 iRow, - int isReadonly -){ - IncrblobChannel *p; - sqlite4 *db = pDb->db; - sqlite4_blob *pBlob; - int rc; - int flags = TCL_READABLE|(isReadonly ? 0 : TCL_WRITABLE); - - /* This variable is used to name the channels: "incrblob_[incr count]" */ - static int count = 0; - char zChannel[64]; - - rc = sqlite4_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob); - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite4_errmsg(pDb->db), TCL_VOLATILE); - return TCL_ERROR; - } - - p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel)); - p->iSeek = 0; - p->pBlob = pBlob; - - sqlite4_snprintf(sizeof(zChannel), zChannel, "incrblob_%d", ++count); - p->channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags); - Tcl_RegisterChannel(interp, p->channel); - - /* Link the new channel into the SqliteDb.pIncrblob list. */ - p->pNext = pDb->pIncrblob; - p->pPrev = 0; - if( p->pNext ){ - p->pNext->pPrev = p; - } - pDb->pIncrblob = p; - p->pDb = pDb; - - Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE); - return TCL_OK; -} -#else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */ - #define closeIncrblobChannels(pDb) -#endif /* ** Look at the script prefix in pCmd. We will be executing this script ** after first appending one or more arguments. This routine analyzes ** the script to see if it is safe to use Tcl_EvalObjv() on the script @@ -471,11 +240,10 @@ ** deleted. */ static void DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); - closeIncrblobChannels(pDb); sqlite4_close(pDb->db); while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; Tcl_DecrRefCount(pFunc->pScript); @@ -1625,36 +1393,34 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ SqliteDb *pDb = (SqliteDb*)cd; int choice; int rc = TCL_OK; static const char *DB_strs[] = { - "authorizer", "backup", "busy", - "cache", "changes", "close", - "collate", "collation_needed", "commit_hook", - "complete", "copy", "enable_load_extension", - "errorcode", "eval", "exists", - "function", "incrblob", "interrupt", - "last_insert_rowid", "nullvalue", "onecolumn", - "profile", "rekey", - "restore", "rollback_hook", "status", - "timeout", "total_changes", "trace", - "transaction", "unlock_notify", "update_hook", - "version", "wal_hook", 0 + "authorizer", "busy", "cache", + "changes", "close", "collate", + "collation_needed", "commit_hook", "complete", + "copy", "enable_load_extension","errorcode", + "eval", "exists", "function", + "interrupt", "last_insert_rowid", "nullvalue", + "onecolumn", "profile", "rekey", + "rollback_hook", "status", "timeout", + "total_changes", "trace", "transaction", + "unlock_notify", "update_hook", "version", + "wal_hook", 0 }; enum DB_enum { - DB_AUTHORIZER, DB_BACKUP, DB_BUSY, - DB_CACHE, DB_CHANGES, DB_CLOSE, - DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, - DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION, - DB_ERRORCODE, DB_EVAL, DB_EXISTS, - DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT, - DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN, - DB_PROFILE, DB_REKEY, - DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, - DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, - DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, - DB_VERSION, DB_WAL_HOOK + DB_AUTHORIZER, DB_BUSY, DB_CACHE, + DB_CHANGES, DB_CLOSE, DB_COLLATE, + DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE, + DB_COPY, DB_ENABLE_LOAD_EXTENSION,DB_ERRORCODE, + DB_EVAL, DB_EXISTS, DB_FUNCTION, + DB_INTERRUPT, DB_LAST_INSERT_ROWID,DB_NULLVALUE, + DB_ONECOLUMN, DB_PROFILE, DB_REKEY, + DB_ROLLBACK_HOOK, DB_STATUS, DB_TIMEOUT, + DB_TOTAL_CHANGES, DB_TRACE, DB_TRANSACTION, + DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, + DB_WAL_HOOK }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); @@ -1718,59 +1484,10 @@ } #endif break; } - /* $db backup ?DATABASE? FILENAME - ** - ** Open or create a database file named FILENAME. Transfer the - ** content of local database DATABASE (default: "main") into the - ** FILENAME database. - */ - case DB_BACKUP: { - const char *zDestFile; - const char *zSrcDb; - sqlite4 *pDest; - sqlite4_backup *pBackup; - - if( objc==3 ){ - zSrcDb = "main"; - zDestFile = Tcl_GetString(objv[2]); - }else if( objc==4 ){ - zSrcDb = Tcl_GetString(objv[2]); - zDestFile = Tcl_GetString(objv[3]); - }else{ - Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); - return TCL_ERROR; - } - rc = sqlite4_open(zDestFile, &pDest); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "cannot open target database: ", - sqlite4_errmsg(pDest), (char*)0); - sqlite4_close(pDest); - return TCL_ERROR; - } - pBackup = sqlite4_backup_init(pDest, "main", pDb->db, zSrcDb); - if( pBackup==0 ){ - Tcl_AppendResult(interp, "backup failed: ", - sqlite4_errmsg(pDest), (char*)0); - sqlite4_close(pDest); - return TCL_ERROR; - } - while( (rc = sqlite4_backup_step(pBackup,100))==SQLITE_OK ){} - sqlite4_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = TCL_OK; - }else{ - Tcl_AppendResult(interp, "backup failed: ", - sqlite4_errmsg(pDest), (char*)0); - rc = TCL_ERROR; - } - sqlite4_close(pDest); - break; - } - /* $db busy ?CALLBACK? ** ** Invoke the given callback if an SQL statement attempts to open ** a locked database file. */ @@ -2187,26 +1904,13 @@ ** ** Turn the extension loading feature on or off. It if off by ** default. */ case DB_ENABLE_LOAD_EXTENSION: { -#ifndef SQLITE_OMIT_LOAD_EXTENSION - int onoff; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "BOOLEAN"); - return TCL_ERROR; - } - if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){ - return TCL_ERROR; - } - sqlite4_enable_load_extension(pDb->db, onoff); - break; -#else Tcl_AppendResult(interp, "extension loading is turned off at compile-time", 0); return TCL_ERROR; -#endif } /* ** $db errorcode ** @@ -2353,50 +2057,10 @@ Tcl_SetResult(interp, (char *)sqlite4_errmsg(pDb->db), TCL_VOLATILE); } break; } - /* - ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID - */ - case DB_INCRBLOB: { -#ifdef SQLITE_OMIT_INCRBLOB - Tcl_AppendResult(interp, "incrblob not available in this build", 0); - return TCL_ERROR; -#else - int isReadonly = 0; - const char *zDb = "main"; - const char *zTable; - const char *zColumn; - sqlite_int64 iRow; - - /* Check for the -readonly option */ - if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){ - isReadonly = 1; - } - - if( objc!=(5+isReadonly) && objc!=(6+isReadonly) ){ - Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID"); - return TCL_ERROR; - } - - if( objc==(6+isReadonly) ){ - zDb = Tcl_GetString(objv[2]); - } - zTable = Tcl_GetString(objv[objc-3]); - zColumn = Tcl_GetString(objv[objc-2]); - rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow); - - if( rc==TCL_OK ){ - rc = createIncrblobChannel( - interp, pDb, zDb, zTable, zColumn, iRow, isReadonly - ); - } -#endif - break; - } - /* ** $db interrupt ** ** Interrupt the execution of the inner-most SQL interpreter. This ** causes the SQL statement to return an error of SQLITE_INTERRUPT. @@ -2522,69 +2186,10 @@ } #endif break; } - /* $db restore ?DATABASE? FILENAME - ** - ** Open a database file named FILENAME. Transfer the content - ** of FILENAME into the local database DATABASE (default: "main"). - */ - case DB_RESTORE: { - const char *zSrcFile; - const char *zDestDb; - sqlite4 *pSrc; - sqlite4_backup *pBackup; - int nTimeout = 0; - - if( objc==3 ){ - zDestDb = "main"; - zSrcFile = Tcl_GetString(objv[2]); - }else if( objc==4 ){ - zDestDb = Tcl_GetString(objv[2]); - zSrcFile = Tcl_GetString(objv[3]); - }else{ - Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); - return TCL_ERROR; - } - rc = sqlite4_open_v2(zSrcFile, &pSrc, SQLITE_OPEN_READONLY, 0); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "cannot open source database: ", - sqlite4_errmsg(pSrc), (char*)0); - sqlite4_close(pSrc); - return TCL_ERROR; - } - pBackup = sqlite4_backup_init(pDb->db, zDestDb, pSrc, "main"); - if( pBackup==0 ){ - Tcl_AppendResult(interp, "restore failed: ", - sqlite4_errmsg(pDb->db), (char*)0); - sqlite4_close(pSrc); - return TCL_ERROR; - } - while( (rc = sqlite4_backup_step(pBackup,100))==SQLITE_OK - || rc==SQLITE_BUSY ){ - if( rc==SQLITE_BUSY ){ - if( nTimeout++ >= 3 ) break; - sqlite4_sleep(100); - } - } - sqlite4_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = TCL_OK; - }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - Tcl_AppendResult(interp, "restore failed: source database busy", - (char*)0); - rc = TCL_ERROR; - }else{ - Tcl_AppendResult(interp, "restore failed: ", - sqlite4_errmsg(pDb->db), (char*)0); - rc = TCL_ERROR; - } - sqlite4_close(pSrc); - break; - } - /* ** $db status (step|sort|autoindex) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. @@ -3621,19 +3226,17 @@ #ifdef SQLITE_TEST { extern int Sqliteconfig_Init(Tcl_Interp*); extern int Sqlitetest1_Init(Tcl_Interp*); - extern int Sqlitetest2_Init(Tcl_Interp*); extern int Sqlitetest3_Init(Tcl_Interp*); extern int Sqlitetest4_Init(Tcl_Interp*); extern int Sqlitetest5_Init(Tcl_Interp*); extern int Sqlitetest6_Init(Tcl_Interp*); extern int Sqlitetest7_Init(Tcl_Interp*); extern int Sqlitetest8_Init(Tcl_Interp*); extern int Sqlitetest9_Init(Tcl_Interp*); - extern int Sqlitetest_autoext_Init(Tcl_Interp*); extern int Sqlitetest_demovfs_Init(Tcl_Interp *); extern int Sqlitetest_func_Init(Tcl_Interp*); extern int Sqlitetest_hexio_Init(Tcl_Interp*); extern int Sqlitetest_init_Init(Tcl_Interp*); extern int Sqlitetest_malloc_Init(Tcl_Interp*); @@ -3642,11 +3245,10 @@ extern int Sqlitetestsse_Init(Tcl_Interp*); extern int Sqlitetesttclvar_Init(Tcl_Interp*); extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); - extern int Sqlitetestbackup_Init(Tcl_Interp*); extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); @@ -3664,37 +3266,29 @@ Zipvfs_Init(interp); #endif Sqliteconfig_Init(interp); Sqlitetest1_Init(interp); - Sqlitetest2_Init(interp); - Sqlitetest3_Init(interp); Sqlitetest4_Init(interp); Sqlitetest5_Init(interp); Sqlitetest6_Init(interp); Sqlitetest7_Init(interp); Sqlitetest8_Init(interp); Sqlitetest9_Init(interp); - Sqlitetest_autoext_Init(interp); Sqlitetest_demovfs_Init(interp); - Sqlitetest_func_Init(interp); Sqlitetest_hexio_Init(interp); Sqlitetest_init_Init(interp); Sqlitetest_malloc_Init(interp); Sqlitetest_mutex_Init(interp); Sqlitetestschema_Init(interp); Sqlitetesttclvar_Init(interp); SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); - Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); Sqlitetestrtree_Init(interp); - Sqlitequota_Init(interp); - SqliteSuperlock_Init(interp); - SqlitetestSyscall_Init(interp); Sqlitetestfuzzer_Init(interp); Sqlitetestwholenumber_Init(interp); Sqliteteststorage_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -1460,48 +1460,10 @@ z = sqlite4_mprintf(argv[1], r); Tcl_AppendResult(interp, z, 0); sqlite4_free(z); return TCL_OK; } - -/* -** Usage: sqlite4_enable_shared_cache ?BOOLEAN? -** -*/ -#if !defined(SQLITE_OMIT_SHARED_CACHE) -static int test_enable_shared( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - int rc; - int enable; - int ret = 0; - - if( objc!=2 && objc!=1 ){ - Tcl_WrongNumArgs(interp, 1, objv, "?BOOLEAN?"); - return TCL_ERROR; - } - ret = sqlite4GlobalConfig.sharedCacheEnabled; - - if( objc==2 ){ - if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ){ - return TCL_ERROR; - } - rc = sqlite4_enable_shared_cache(enable); - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite4ErrStr(rc), TCL_STATIC); - return TCL_ERROR; - } - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ret)); - return TCL_OK; -} -#endif - - /* ** Usage: sqlite4_extended_result_codes DB BOOLEAN ** */ @@ -1591,216 +1553,10 @@ return TCL_OK; } #endif -#ifndef SQLITE_OMIT_INCRBLOB - -static int blobHandleFromObj( - Tcl_Interp *interp, - Tcl_Obj *pObj, - sqlite4_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 = *((sqlite4_blob **)instanceData); - } - - return TCL_OK; -} - -/* -** sqlite4_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 */ -){ - sqlite4_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 = sqlite4_blob_bytes(pBlob); - Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte)); - - return TCL_OK; -} - -/* -** sqlite4_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 */ -){ - sqlite4_blob *pBlob; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL"); - return TCL_ERROR; - } - - if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; - sqlite4_blob_close(pBlob); - - return TCL_OK; -} - -/* -** sqlite4_blob_read CHANNEL OFFSET N -** -** This command is used to test the sqlite4_blob_read() in ways that -** the Tcl channel interface does not. The first argument should -** be the name of a valid channel created by the [incrblob] method -** of a database handle. This function calls sqlite4_blob_read() -** to read N bytes from offset OFFSET from the underlying SQLite -** blob handle. -** -** On success, a byte-array object containing the read data is -** returned. On failure, the interpreter result is set to the -** text representation of the returned error code (i.e. "SQLITE_NOMEM") -** and a Tcl exception is thrown. -*/ -static int test_blob_read( - 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 */ -){ - sqlite4_blob *pBlob; - int nByte; - int iOffset; - unsigned char *zBuf = 0; - int rc; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N"); - return TCL_ERROR; - } - - 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) - ){ - return TCL_ERROR; - } - - if( nByte>0 ){ - zBuf = (unsigned char *)Tcl_Alloc(nByte); - } - rc = sqlite4_blob_read(pBlob, zBuf, nByte, iOffset); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte)); - }else{ - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE); - } - Tcl_Free((char *)zBuf); - - return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); -} - -/* -** sqlite4_blob_write CHANNEL OFFSET DATA ?NDATA? -** -** This command is used to test the sqlite4_blob_write() in ways that -** the Tcl channel interface does not. The first argument should -** be the name of a valid channel created by the [incrblob] method -** of a database handle. This function calls sqlite4_blob_write() -** to write the DATA byte-array to the underlying SQLite blob handle. -** at offset OFFSET. -** -** On success, an empty string is returned. On failure, the interpreter -** result is set to the text representation of the returned error code -** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown. -*/ -static int test_blob_write( - 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 */ -){ - sqlite4_blob *pBlob; - int iOffset; - int rc; - - unsigned char *zBuf; - int nBuf; - - if( objc!=4 && objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?"); - return TCL_ERROR; - } - - if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; - if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){ - return TCL_ERROR; - } - - zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf); - if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){ - return TCL_ERROR; - } - rc = sqlite4_blob_write(pBlob, zBuf, nBuf, iOffset); - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE); - } - - 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; - sqlite4_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 = sqlite4_blob_reopen(pBlob, iRowid); - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE); - } - - return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); -} - -#endif /* ** Usage: sqlite4_create_collation DB-HANDLE NAME CMP-PROC DEL-PROC ** ** This Tcl proc is used for testing the experimental @@ -2006,107 +1762,10 @@ return TCL_ERROR; } return TCL_OK; } -/* -** Usage: sqlite4_load_extension DB-HANDLE FILE ?PROC? -*/ -static int test_load_extension( - 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_CmdInfo cmdInfo; - sqlite4 *db; - int rc; - char *zDb; - char *zFile; - char *zProc = 0; - char *zErr = 0; - - if( objc!=4 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE FILE ?PROC?"); - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[1]); - zFile = Tcl_GetString(objv[2]); - if( objc==4 ){ - zProc = Tcl_GetString(objv[3]); - } - - /* Extract the C database handle from the Tcl command name */ - if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ - Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0); - return TCL_ERROR; - } - db = ((struct SqliteDb*)cmdInfo.objClientData)->db; - assert(db); - - /* Call the underlying C function. If an error occurs, set rc to - ** TCL_ERROR and load any error string into the interpreter. If no - ** error occurs, set rc to TCL_OK. - */ -#ifdef SQLITE_OMIT_LOAD_EXTENSION - rc = SQLITE_ERROR; - zErr = sqlite4_mprintf("this build omits sqlite4_load_extension()"); -#else - rc = sqlite4_load_extension(db, zFile, zProc, &zErr); -#endif - if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, zErr ? zErr : "", TCL_VOLATILE); - rc = TCL_ERROR; - }else{ - rc = TCL_OK; - } - sqlite4_free(zErr); - - return rc; -} - -/* -** Usage: sqlite4_enable_load_extension DB-HANDLE ONOFF -*/ -static int test_enable_load( - 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_CmdInfo cmdInfo; - sqlite4 *db; - char *zDb; - int onoff; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE ONOFF"); - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[1]); - - /* Extract the C database handle from the Tcl command name */ - if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ - Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0); - return TCL_ERROR; - } - db = ((struct SqliteDb*)cmdInfo.objClientData)->db; - assert(db); - - /* Get the onoff parameter */ - if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){ - return TCL_ERROR; - } - -#ifdef SQLITE_OMIT_LOAD_EXTENSION - Tcl_AppendResult(interp, "this build omits sqlite4_load_extension()"); - return TCL_ERROR; -#else - sqlite4_enable_load_extension(db, onoff); - return TCL_OK; -#endif -} /* ** Usage: sqlite_abort ** ** Shutdown the process immediately. This is not a clean shutdown. @@ -2378,11 +2037,11 @@ return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = sqlite4_stmt_readonly(pStmt); - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal)); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->needSavepoint)); return TCL_OK; } /* @@ -4627,33 +4286,10 @@ rc = sqlite4_db_release_memory(db); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } -/* -** Usage: sqlite4_db_filename DB DBNAME -** -** Return the name of a file associated with a database. -*/ -static int test_db_filename( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4 *db; - const char *zDbName; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; - zDbName = Tcl_GetString(objv[2]); - Tcl_AppendResult(interp, sqlite4_db_filename(db, zDbName), (void*)0); - return TCL_OK; -} - /* ** Usage: sqlite4_soft_heap_limit ?N? ** ** Query or set the soft heap limit for the current thread. The ** limit is only changed if the N is present. The previous limit @@ -4677,66 +4313,10 @@ amt = sqlite4_soft_heap_limit64(N); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(amt)); return TCL_OK; } -/* -** Usage: sqlite4_thread_cleanup -** -** Call the sqlite4_thread_cleanup API. -*/ -static int test_thread_cleanup( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_thread_cleanup(); -#endif - return TCL_OK; -} - -/* -** Usage: sqlite4_pager_refcounts DB -** -** Return a list of numbers which are the PagerRefcount for all -** pagers on each database connection. -*/ -static int test_pager_refcounts( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4 *db; - int i; - int v, *a; - Tcl_Obj *pResult; - - if( objc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetStringFromObj(objv[0], 0), " DB", 0); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; - pResult = Tcl_NewObj(); - for(i=0; inDb; i++){ - if( db->aDb[i].pBt==0 ){ - v = -1; - }else{ - sqlite4_mutex_enter(db->mutex); - a = sqlite4PagerStats(sqlite4BtreePager(db->aDb[i].pBt)); - v = a[0]; - sqlite4_mutex_leave(db->mutex); - } - Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(v)); - } - Tcl_SetObjResult(interp, pResult); - return TCL_OK; -} - /* ** tclcmd: working_64bit_int ** ** Some TCL builds (ex: cygwin) do not support 64-bit integers. This @@ -4935,357 +4515,10 @@ } return TCL_OK; } -/* -** tclcmd: file_control_test DB -** -** This TCL command runs the sqlite4_file_control interface and -** verifies correct operation of the same. -*/ -static int file_control_test( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - int iArg = 0; - sqlite4 *db; - int rc; - - if( objc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetStringFromObj(objv[0], 0), " DB", 0); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; - rc = sqlite4_file_control(db, 0, 0, &iArg); - assert( rc==SQLITE_NOTFOUND ); - rc = sqlite4_file_control(db, "notadatabase", SQLITE_FCNTL_LOCKSTATE, &iArg); - assert( rc==SQLITE_ERROR ); - rc = sqlite4_file_control(db, "main", -1, &iArg); - assert( rc==SQLITE_NOTFOUND ); - rc = sqlite4_file_control(db, "temp", -1, &iArg); - assert( rc==SQLITE_NOTFOUND || rc==SQLITE_ERROR ); - - return TCL_OK; -} - - -/* -** tclcmd: file_control_lasterrno_test DB -** -** This TCL command runs the sqlite4_file_control interface and -** verifies correct operation of the SQLITE_LAST_ERRNO verb. -*/ -static int file_control_lasterrno_test( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - int iArg = 0; - sqlite4 *db; - int rc; - - if( objc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetStringFromObj(objv[0], 0), " DB", 0); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - rc = sqlite4_file_control(db, NULL, SQLITE_LAST_ERRNO, &iArg); - if( rc ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_ERROR; - } - if( iArg!=0 ) { - Tcl_AppendResult(interp, "Unexpected non-zero errno: ", - Tcl_GetStringFromObj(Tcl_NewIntObj(iArg), 0), " ", 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** tclcmd: file_control_chunksize_test DB DBNAME SIZE -** -** This TCL command runs the sqlite4_file_control interface and -** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and -** SQLITE_SET_LOCKPROXYFILE verbs. -*/ -static int file_control_chunksize_test( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - int nSize; /* New chunk size */ - char *zDb; /* Db name ("main", "temp" etc.) */ - sqlite4 *db; /* Database handle */ - int rc; /* file_control() return code */ - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) - || Tcl_GetIntFromObj(interp, objv[3], &nSize) - ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - if( zDb[0]=='\0' ) zDb = NULL; - - rc = sqlite4_file_control(db, zDb, SQLITE_FCNTL_CHUNK_SIZE, (void *)&nSize); - if( rc ){ - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** tclcmd: file_control_sizehint_test DB DBNAME SIZE -** -** This TCL command runs the sqlite4_file_control interface -** with SQLITE_FCNTL_SIZE_HINT -*/ -static int file_control_sizehint_test( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - sqlite4_int64 nSize; /* Hinted size */ - char *zDb; /* Db name ("main", "temp" etc.) */ - sqlite4 *db; /* Database handle */ - int rc; /* file_control() return code */ - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) - || Tcl_GetWideIntFromObj(interp, objv[3], &nSize) - ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - if( zDb[0]=='\0' ) zDb = NULL; - - rc = sqlite4_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize); - if( rc ){ - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** tclcmd: file_control_lockproxy_test DB PWD -** -** This TCL command runs the sqlite4_file_control interface and -** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and -** SQLITE_SET_LOCKPROXYFILE verbs. -*/ -static int file_control_lockproxy_test( - ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - sqlite4 *db; - const char *zPwd; - int nPwd; - - if( objc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetStringFromObj(objv[0], 0), " DB PWD", 0); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - zPwd = Tcl_GetStringFromObj(objv[2], &nPwd); - -#if !defined(SQLITE_ENABLE_LOCKING_STYLE) -# if defined(__APPLE__) -# define SQLITE_ENABLE_LOCKING_STYLE 1 -# else -# define SQLITE_ENABLE_LOCKING_STYLE 0 -# endif -#endif -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - { - char *testPath; - int rc; - char proxyPath[400]; - - if( sizeof(proxyPath) -#include -#include - -/* -** Interpret an SQLite error number -*/ -static char *errorName(int rc){ - char *zName; - switch( rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - default: zName = "SQLITE_Unknown"; break; - } - return zName; -} - -/* -** Page size and reserved size used for testing. -*/ -static int test_pagesize = 1024; - -/* -** Dummy page reinitializer -*/ -static void pager_test_reiniter(DbPage *pNotUsed){ - return; -} - -/* -** Usage: pager_open FILENAME N-PAGE -** -** Open a new pager -*/ -static int pager_open( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - u32 pageSize; - Pager *pPager; - int nPage; - int rc; - char zBuf[100]; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " FILENAME N-PAGE\"", 0); - return TCL_ERROR; - } - if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR; - rc = sqlite4PagerOpen(sqlite4_vfs_find(0), &pPager, argv[1], 0, 0, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB, - pager_test_reiniter); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4PagerSetCachesize(pPager, nPage); - pageSize = test_pagesize; - sqlite4PagerSetPagesize(pPager, &pageSize, -1); - sqlite4_snprintf(sizeof(zBuf),zBuf,"%p",pPager); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: pager_close ID -** -** Close the given pager. -*/ -static int pager_close( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerClose(pPager); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_rollback ID -** -** Rollback changes -*/ -static int pager_rollback( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerRollback(pPager); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_commit ID -** -** Commit all changes -*/ -static int pager_commit( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerCommitPhaseOne(pPager, 0, 0); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - rc = sqlite4PagerCommitPhaseTwo(pPager); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_stmt_begin ID -** -** Start a new checkpoint. -*/ -static int pager_stmt_begin( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerOpenSavepoint(pPager, 1); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_stmt_rollback ID -** -** Rollback changes to a checkpoint -*/ -static int pager_stmt_rollback( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0); - sqlite4PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_stmt_commit ID -** -** Commit changes to a checkpoint -*/ -static int pager_stmt_commit( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: pager_stats ID -** -** Return pager statistics. -*/ -static int pager_stats( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int i, *a; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - a = sqlite4PagerStats(pPager); - for(i=0; i<9; i++){ - static char *zName[] = { - "ref", "page", "max", "size", "state", "err", - "hit", "miss", "ovfl", - }; - char zBuf[100]; - Tcl_AppendElement(interp, zName[i]); - sqlite4_snprintf(sizeof(zBuf),zBuf,"%d",a[i]); - Tcl_AppendElement(interp, zBuf); - } - return TCL_OK; -} - -/* -** Usage: pager_pagecount ID -** -** Return the size of the database file. -*/ -static int pager_pagecount( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - char zBuf[100]; - int nPage; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - sqlite4PagerPagecount(pPager, &nPage); - sqlite4_snprintf(sizeof(zBuf), zBuf, "%d", nPage); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: page_get ID PGNO -** -** Return a pointer to a page from the database. -*/ -static int page_get( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - char zBuf[100]; - DbPage *pPage; - int pgno; - int rc; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID PGNO\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - rc = sqlite4PagerSharedLock(pPager); - if( rc==SQLITE_OK ){ - rc = sqlite4PagerGet(pPager, pgno, &pPage); - } - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4_snprintf(sizeof(zBuf),zBuf,"%p",pPage); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: page_lookup ID PGNO -** -** Return a pointer to a page if the page is already in cache. -** If not in cache, return an empty string. -*/ -static int page_lookup( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - char zBuf[100]; - DbPage *pPage; - int pgno; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID PGNO\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - pPage = sqlite4PagerLookup(pPager, pgno); - if( pPage ){ - sqlite4_snprintf(sizeof(zBuf),zBuf,"%p",pPage); - Tcl_AppendResult(interp, zBuf, 0); - } - return TCL_OK; -} - -/* -** Usage: pager_truncate ID PGNO -*/ -static int pager_truncate( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Pager *pPager; - int pgno; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID PGNO\"", 0); - return TCL_ERROR; - } - pPager = sqlite4TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - sqlite4PagerTruncateImage(pPager, pgno); - return TCL_OK; -} - - -/* -** Usage: page_unref PAGE -** -** Drop a pointer to a page. -*/ -static int page_unref( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - DbPage *pPage; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " PAGE\"", 0); - return TCL_ERROR; - } - pPage = (DbPage *)sqlite4TestTextToPtr(argv[1]); - sqlite4PagerUnref(pPage); - return TCL_OK; -} - -/* -** Usage: page_read PAGE -** -** Return the content of a page -*/ -static int page_read( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - char zBuf[100]; - DbPage *pPage; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " PAGE\"", 0); - return TCL_ERROR; - } - pPage = sqlite4TestTextToPtr(argv[1]); - memcpy(zBuf, sqlite4PagerGetData(pPage), sizeof(zBuf)); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: page_number PAGE -** -** Return the page number for a page. -*/ -static int page_number( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - char zBuf[100]; - DbPage *pPage; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " PAGE\"", 0); - return TCL_ERROR; - } - pPage = (DbPage *)sqlite4TestTextToPtr(argv[1]); - sqlite4_snprintf(sizeof(zBuf), zBuf, "%d", sqlite4PagerPagenumber(pPage)); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: page_write PAGE DATA -** -** Write something into a page. -*/ -static int page_write( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - DbPage *pPage; - char *pData; - int rc; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " PAGE DATA\"", 0); - return TCL_ERROR; - } - pPage = (DbPage *)sqlite4TestTextToPtr(argv[1]); - rc = sqlite4PagerWrite(pPage); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - pData = sqlite4PagerGetData(pPage); - strncpy(pData, argv[2], test_pagesize-1); - pData[test_pagesize-1] = 0; - return TCL_OK; -} - -#ifndef SQLITE_OMIT_DISKIO -/* -** Usage: fake_big_file N FILENAME -** -** Write a few bytes at the N megabyte point of FILENAME. This will -** create a large file. If the file was a valid SQLite database, then -** the next time the database is opened, SQLite will begin allocating -** new pages after N. If N is 2096 or bigger, this will test the -** ability of SQLite to write to large files. -*/ -static int fake_big_file( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - sqlite4_vfs *pVfs; - sqlite4_file *fd = 0; - int rc; - int n; - i64 offset; - char *zFile; - int nFile; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " N-MEGABYTES FILE\"", 0); - return TCL_ERROR; - } - if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; - - pVfs = sqlite4_vfs_find(0); - nFile = strlen(argv[2]); - zFile = sqlite4_malloc( nFile+2 ); - if( zFile==0 ) return TCL_ERROR; - memcpy(zFile, argv[2], nFile+1); - zFile[nFile+1] = 0; - rc = sqlite4OsOpenMalloc(pVfs, zFile, &fd, - (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0 - ); - if( rc ){ - Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0); - sqlite4_free(zFile); - return TCL_ERROR; - } - offset = n; - offset *= 1024*1024; - rc = sqlite4OsWrite(fd, "Hello, World!", 14, offset); - sqlite4OsCloseFree(fd); - sqlite4_free(zFile); - if( rc ){ - Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} -#endif - - -/* -** test_control_pending_byte PENDING_BYTE -** -** Set the PENDING_BYTE using the sqlite4_test_control() interface. -*/ -static int testPendingByte( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - int pbyte; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " PENDING-BYTE\"", (void*)0); - return TCL_ERROR; - } - if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR; - rc = sqlite4_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** sqlite4BitvecBuiltinTest SIZE PROGRAM -** -** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control. -** See comments on sqlite4BitvecBuiltinTest() for additional information. -*/ -static int testBitvecBuiltinTest( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - int sz, rc; - int nProg = 0; - int aProg[100]; - const char *z; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " SIZE PROGRAM\"", (void*)0); - } - if( Tcl_GetInt(interp, argv[1], &sz) ) return TCL_ERROR; - z = argv[2]; - while( nProg<99 && *z ){ - while( *z && !sqlite4Isdigit(*z) ){ z++; } - if( *z==0 ) break; - aProg[nProg++] = atoi(z); - while( sqlite4Isdigit(*z) ){ z++; } - } - aProg[nProg] = 0; - rc = sqlite4_test_control(SQLITE_TESTCTRL_BITVEC_TEST, sz, aProg); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** Register commands with the TCL interpreter. -*/ -int Sqlitetest2_Init(Tcl_Interp *interp){ - extern int sqlite4_io_error_persist; - extern int sqlite4_io_error_pending; - extern int sqlite4_io_error_hit; - extern int sqlite4_io_error_hardhit; - extern int sqlite4_diskfull_pending; - extern int sqlite4_diskfull; - static struct { - char *zName; - Tcl_CmdProc *xProc; - } aCmd[] = { - { "pager_open", (Tcl_CmdProc*)pager_open }, - { "pager_close", (Tcl_CmdProc*)pager_close }, - { "pager_commit", (Tcl_CmdProc*)pager_commit }, - { "pager_rollback", (Tcl_CmdProc*)pager_rollback }, - { "pager_stmt_begin", (Tcl_CmdProc*)pager_stmt_begin }, - { "pager_stmt_commit", (Tcl_CmdProc*)pager_stmt_commit }, - { "pager_stmt_rollback", (Tcl_CmdProc*)pager_stmt_rollback }, - { "pager_stats", (Tcl_CmdProc*)pager_stats }, - { "pager_pagecount", (Tcl_CmdProc*)pager_pagecount }, - { "page_get", (Tcl_CmdProc*)page_get }, - { "page_lookup", (Tcl_CmdProc*)page_lookup }, - { "page_unref", (Tcl_CmdProc*)page_unref }, - { "page_read", (Tcl_CmdProc*)page_read }, - { "page_write", (Tcl_CmdProc*)page_write }, - { "page_number", (Tcl_CmdProc*)page_number }, - { "pager_truncate", (Tcl_CmdProc*)pager_truncate }, -#ifndef SQLITE_OMIT_DISKIO - { "fake_big_file", (Tcl_CmdProc*)fake_big_file }, -#endif - { "sqlite4BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest }, - { "sqlite4_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte }, - }; - int i; - for(i=0; i -#include - -/* -** Interpret an SQLite error number -*/ -static char *errorName(int rc){ - char *zName; - switch( rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - default: zName = "SQLITE_Unknown"; break; - } - return zName; -} - -/* -** A bogus sqlite4 connection structure for use in the btree -** tests. -*/ -static sqlite4 sDb; -static int nRefSqlite3 = 0; - -/* -** Usage: btree_open FILENAME NCACHE -** -** Open a new database -*/ -static int btree_open( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc, nCache; - char zBuf[100]; - int n; - char *zFilename; - 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; - nRefSqlite3++; - if( nRefSqlite3==1 ){ - sDb.pVfs = sqlite4_vfs_find(0); - sDb.mutex = sqlite4MutexAlloc(SQLITE_MUTEX_RECURSIVE); - sqlite4_mutex_enter(sDb.mutex); - } - n = strlen(argv[1]); - zFilename = sqlite4_malloc( n+2 ); - if( zFilename==0 ) return TCL_ERROR; - memcpy(zFilename, argv[1], n+1); - zFilename[n+1] = 0; - rc = sqlite4BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); - sqlite4_free(zFilename); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4BtreeSetCacheSize(pBt, nCache); - sqlite4_snprintf(sizeof(zBuf), zBuf,"%p", pBt); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: btree_close ID -** -** Close the given database. -*/ -static int btree_close( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - rc = sqlite4BtreeClose(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - nRefSqlite3--; - if( nRefSqlite3==0 ){ - sqlite4_mutex_leave(sDb.mutex); - sqlite4_mutex_free(sDb.mutex); - sDb.mutex = 0; - sDb.pVfs = 0; - } - return TCL_OK; -} - - -/* -** Usage: btree_begin_transaction ID -** -** Start a new transaction -*/ -static int btree_begin_transaction( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - sqlite4BtreeEnter(pBt); - rc = sqlite4BtreeBeginTrans(pBt, 1); - sqlite4BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_pager_stats ID -** -** Returns pager statistics -*/ -static int btree_pager_stats( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int i; - int *a; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - - /* Normally in this file, with a b-tree handle opened using the - ** [btree_open] command it is safe to call sqlite4BtreeEnter() directly. - ** But this function is sometimes called with a btree handle obtained - ** from an open SQLite connection (using [btree_from_db]). In this case - ** we need to obtain the mutex for the controlling SQLite handle before - ** it is safe to call sqlite4BtreeEnter(). - */ - sqlite4_mutex_enter(pBt->db->mutex); - - sqlite4BtreeEnter(pBt); - a = sqlite4PagerStats(sqlite4BtreePager(pBt)); - for(i=0; i<11; i++){ - static char *zName[] = { - "ref", "page", "max", "size", "state", "err", - "hit", "miss", "ovfl", "read", "write" - }; - char zBuf[100]; - Tcl_AppendElement(interp, zName[i]); - sqlite4_snprintf(sizeof(zBuf), zBuf,"%d",a[i]); - Tcl_AppendElement(interp, zBuf); - } - sqlite4BtreeLeave(pBt); - - /* Release the mutex on the SQLite handle that controls this b-tree */ - sqlite4_mutex_leave(pBt->db->mutex); - return TCL_OK; -} - -/* -** Usage: btree_cursor ID TABLENUM WRITEABLE -** -** Create a new cursor. Return the ID for the cursor. -*/ -static int btree_cursor( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int iTable; - BtCursor *pCur; - int rc = SQLITE_OK; - int wrFlag; - char zBuf[30]; - - if( argc!=4 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM WRITEABLE\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR; - pCur = (BtCursor *)ckalloc(sqlite4BtreeCursorSize()); - memset(pCur, 0, sqlite4BtreeCursorSize()); - sqlite4BtreeEnter(pBt); -#ifndef SQLITE_OMIT_SHARED_CACHE - rc = sqlite4BtreeLockTable(pBt, iTable, wrFlag); -#endif - if( rc==SQLITE_OK ){ - rc = sqlite4BtreeCursor(pBt, iTable, wrFlag, 0, pCur); - } - sqlite4BtreeLeave(pBt); - if( rc ){ - ckfree((char *)pCur); - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4_snprintf(sizeof(zBuf), zBuf,"%p", pCur); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_close_cursor ID -** -** Close a cursor opened using btree_cursor. -*/ -static int btree_close_cursor( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - Btree *pBt; - int rc; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite4TestTextToPtr(argv[1]); - pBt = pCur->pBtree; - sqlite4BtreeEnter(pBt); - rc = sqlite4BtreeCloseCursor(pCur); - sqlite4BtreeLeave(pBt); - ckfree((char *)pCur); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return SQLITE_OK; -} - -/* -** Usage: btree_next ID -** -** Move the cursor to the next entry in the table. Return 0 on success -** or 1 if the cursor was already on the last entry in the table or if -** the table is empty. -*/ -static int btree_next( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite4TestTextToPtr(argv[1]); - sqlite4BtreeEnter(pCur->pBtree); - rc = sqlite4BtreeNext(pCur, &res); - sqlite4BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_first ID -** -** Move the cursor to the first entry in the table. Return 0 if the -** cursor was left point to something and 1 if the table is empty. -*/ -static int btree_first( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite4TestTextToPtr(argv[1]); - sqlite4BtreeEnter(pCur->pBtree); - rc = sqlite4BtreeFirst(pCur, &res); - sqlite4BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite4_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_eof ID -** -** Return TRUE if the given cursor is not pointing at a valid entry. -** Return FALSE if the cursor does point to a valid entry. -*/ -static int btree_eof( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - char zBuf[50]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite4TestTextToPtr(argv[1]); - sqlite4BtreeEnter(pCur->pBtree); - rc = sqlite4BtreeEof(pCur); - sqlite4BtreeLeave(pCur->pBtree); - sqlite4_snprintf(sizeof(zBuf),zBuf, "%d", rc); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_payload_size ID -** -** Return the number of bytes of payload -*/ -static int btree_payload_size( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int n2; - u64 n1; - char zBuf[50]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite4TestTextToPtr(argv[1]); - sqlite4BtreeEnter(pCur->pBtree); - - /* The cursor may be in "require-seek" state. If this is the case, the - ** call to BtreeDataSize() will fix it. */ - sqlite4BtreeDataSize(pCur, (u32*)&n2); - if( pCur->apPage[pCur->iPage]->intKey ){ - n1 = 0; - }else{ - sqlite4BtreeKeySize(pCur, (i64*)&n1); - } - sqlite4BtreeLeave(pCur->pBtree); - sqlite4_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** usage: varint_test START MULTIPLIER COUNT INCREMENT -** -** This command tests the putVarint() and getVarint() -** routines, both for accuracy and for speed. -** -** An integer is written using putVarint() and read back with -** getVarint() and varified to be unchanged. This repeats COUNT -** times. The first integer is START*MULTIPLIER. Each iteration -** increases the integer by INCREMENT. -** -** This command returns nothing if it works. It returns an error message -** if something goes wrong. -*/ -static int btree_varint_test( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - u32 start, mult, count, incr; - u64 in, out; - int n1, n2, i, j; - unsigned char zBuf[100]; - if( argc!=5 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " START MULTIPLIER COUNT INCREMENT\"", 0); - return TCL_ERROR; - } - if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR; - if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR; - if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR; - if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR; - in = start; - in *= mult; - for(i=0; i9 || n1<1 ){ - sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1); - Tcl_AppendResult(interp, zErr, 0); - return TCL_ERROR; - } - n2 = getVarint(zBuf, &out); - if( n1!=n2 ){ - sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2); - Tcl_AppendResult(interp, zErr, 0); - return TCL_ERROR; - } - if( in!=out ){ - sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out); - Tcl_AppendResult(interp, zErr, 0); - return TCL_ERROR; - } - if( (in & 0xffffffff)==in ){ - u32 out32; - n2 = getVarint32(zBuf, out32); - out = out32; - if( n1!=n2 ){ - sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", - n1, n2); - Tcl_AppendResult(interp, zErr, 0); - return TCL_ERROR; - } - if( in!=out ){ - sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", - in, out); - Tcl_AppendResult(interp, zErr, 0); - return TCL_ERROR; - } - } - - /* In order to get realistic timings, run getVarint 19 more times. - ** This is because getVarint is called about 20 times more often - ** than putVarint. - */ - for(j=0; j<19; j++){ - getVarint(zBuf, &out); - } - in += incr; - } - return TCL_OK; -} - -/* -** usage: btree_from_db DB-HANDLE -** -** This command returns the btree handle for the main database associated -** with the database-handle passed as the argument. Example usage: -** -** sqlite4 db test.db -** set bt [btree_from_db db] -*/ -static int btree_from_db( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - char zBuf[100]; - Tcl_CmdInfo info; - sqlite4 *db; - Btree *pBt; - int iDb = 0; - - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " DB-HANDLE ?N?\"", 0); - return TCL_ERROR; - } - - if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){ - Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0); - return TCL_ERROR; - } - if( argc==3 ){ - iDb = atoi(argv[2]); - } - - db = *((sqlite4 **)info.objClientData); - assert( db ); - - pBt = db->aDb[iDb].pBt; - sqlite4_snprintf(sizeof(zBuf), zBuf, "%p", pBt); - Tcl_SetResult(interp, zBuf, TCL_VOLATILE); - return TCL_OK; -} - -/* -** Usage: btree_ismemdb ID -** -** Return true if the B-Tree is in-memory. -*/ -static int btree_ismemdb( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int res; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - sqlite4_mutex_enter(pBt->db->mutex); - sqlite4BtreeEnter(pBt); - res = sqlite4PagerIsMemdb(sqlite4BtreePager(pBt)); - sqlite4BtreeLeave(pBt); - sqlite4_mutex_leave(pBt->db->mutex); - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); - return SQLITE_OK; -} - -/* -** usage: btree_set_cache_size ID NCACHE -** -** Set the size of the cache used by btree $ID. -*/ -static int btree_set_cache_size( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - int nCache; - Btree *pBt; - - if( argc!=3 ){ - Tcl_AppendResult( - interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0); - return TCL_ERROR; - } - pBt = sqlite4TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; - - sqlite4_mutex_enter(pBt->db->mutex); - sqlite4BtreeEnter(pBt); - sqlite4BtreeSetCacheSize(pBt, nCache); - sqlite4BtreeLeave(pBt); - sqlite4_mutex_leave(pBt->db->mutex); - return TCL_OK; -} - - - -/* -** Register commands with the TCL interpreter. -*/ -int Sqlitetest3_Init(Tcl_Interp *interp){ - static struct { - char *zName; - Tcl_CmdProc *xProc; - } aCmd[] = { - { "btree_open", (Tcl_CmdProc*)btree_open }, - { "btree_close", (Tcl_CmdProc*)btree_close }, - { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, - { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, - { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, - { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, - { "btree_next", (Tcl_CmdProc*)btree_next }, - { "btree_eof", (Tcl_CmdProc*)btree_eof }, - { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, - { "btree_first", (Tcl_CmdProc*)btree_first }, - { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, - { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, - { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, - { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size } - }; - int i; - - for(i=0; izErr && p->zErr!=p->zStaticErr ){ sqlite4_free(p->zErr); p->zErr = 0; } p->completed++; -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_thread_cleanup(); -#endif return 0; } /* ** Get a thread ID which is an upper case letter. Return the index. Index: src/test7.c ================================================================== --- src/test7.c +++ src/test7.c @@ -114,13 +114,10 @@ if( p->zErr && p->zErr!=p->zStaticErr ){ sqlite4_free(p->zErr); p->zErr = 0; } p->completed++; -#ifndef SQLITE_OMIT_DEPRECATED - sqlite4_thread_cleanup(); -#endif return 0; } /* ** Get a thread ID which is an upper case letter. Return the index. DELETED src/test_autoext.c Index: src/test_autoext.c ================================================================== --- src/test_autoext.c +++ /dev/null @@ -1,167 +0,0 @@ -/* -** 2006 August 23 -** -** 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. -** -************************************************************************* -** Test extension for testing the sqlite4_auto_extension() function. -*/ -#include "tcl.h" -#include "sqlite4ext.h" - -#ifndef SQLITE_OMIT_LOAD_EXTENSION -static SQLITE_EXTENSION_INIT1 - -/* -** The sqr() SQL function returns the square of its input value. -*/ -static void sqrFunc( - sqlite4_context *context, - int argc, - sqlite4_value **argv -){ - double r = sqlite4_value_double(argv[0]); - sqlite4_result_double(context, r*r); -} - -/* -** This is the entry point to register the extension for the sqr() function. -*/ -static int sqr_init( - sqlite4 *db, - char **pzErrMsg, - const sqlite4_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi); - sqlite4_create_function(db, "sqr", 1, SQLITE_ANY, 0, sqrFunc, 0, 0); - return 0; -} - -/* -** The cube() SQL function returns the cube of its input value. -*/ -static void cubeFunc( - sqlite4_context *context, - int argc, - sqlite4_value **argv -){ - double r = sqlite4_value_double(argv[0]); - sqlite4_result_double(context, r*r*r); -} - -/* -** This is the entry point to register the extension for the cube() function. -*/ -static int cube_init( - sqlite4 *db, - char **pzErrMsg, - const sqlite4_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi); - sqlite4_create_function(db, "cube", 1, SQLITE_ANY, 0, cubeFunc, 0, 0); - return 0; -} - -/* -** This is a broken extension entry point -*/ -static int broken_init( - sqlite4 *db, - char **pzErrMsg, - const sqlite4_api_routines *pApi -){ - char *zErr; - SQLITE_EXTENSION_INIT2(pApi); - zErr = sqlite4_mprintf("broken autoext!"); - *pzErrMsg = zErr; - return 1; -} - -/* -** tclcmd: sqlite4_auto_extension_sqr -** -** Register the "sqr" extension to be loaded automatically. -*/ -static int autoExtSqrObjCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int rc = sqlite4_auto_extension((void*)sqr_init); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return SQLITE_OK; -} - -/* -** tclcmd: sqlite4_auto_extension_cube -** -** Register the "cube" extension to be loaded automatically. -*/ -static int autoExtCubeObjCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int rc = sqlite4_auto_extension((void*)cube_init); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return SQLITE_OK; -} - -/* -** tclcmd: sqlite4_auto_extension_broken -** -** Register the broken extension to be loaded automatically. -*/ -static int autoExtBrokenObjCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int rc = sqlite4_auto_extension((void*)broken_init); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return SQLITE_OK; -} - -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ - - -/* -** tclcmd: sqlite4_reset_auto_extension -** -** Reset all auto-extensions -*/ -static int resetAutoExtObjCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_reset_auto_extension(); - return SQLITE_OK; -} - - -/* -** This procedure registers the TCL procs defined in this file. -*/ -int Sqlitetest_autoext_Init(Tcl_Interp *interp){ -#ifndef SQLITE_OMIT_LOAD_EXTENSION - Tcl_CreateObjCommand(interp, "sqlite4_auto_extension_sqr", - autoExtSqrObjCmd, 0, 0); - Tcl_CreateObjCommand(interp, "sqlite4_auto_extension_cube", - autoExtCubeObjCmd, 0, 0); - Tcl_CreateObjCommand(interp, "sqlite4_auto_extension_broken", - autoExtBrokenObjCmd, 0, 0); -#endif - Tcl_CreateObjCommand(interp, "sqlite4_reset_auto_extension", - resetAutoExtObjCmd, 0, 0); - return TCL_OK; -} DELETED src/test_backup.c Index: src/test_backup.c ================================================================== --- src/test_backup.c +++ /dev/null @@ -1,148 +0,0 @@ -/* -** 2009 January 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 test logic for the sqlite4_backup() interface. -** -*/ - -#include "tcl.h" -#include -#include - -/* These functions are implemented in test1.c. */ -int getDbPointer(Tcl_Interp *, const char *, sqlite4 **); -const char *sqlite4TestErrorName(int); - -static int backupTestCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const*objv -){ - enum BackupSubCommandEnum { - BACKUP_STEP, BACKUP_FINISH, BACKUP_REMAINING, BACKUP_PAGECOUNT - }; - struct BackupSubCommand { - const char *zCmd; - enum BackupSubCommandEnum eCmd; - int nArg; - const char *zArg; - } aSub[] = { - {"step", BACKUP_STEP , 1, "npage" }, - {"finish", BACKUP_FINISH , 0, "" }, - {"remaining", BACKUP_REMAINING , 0, "" }, - {"pagecount", BACKUP_PAGECOUNT , 0, "" }, - {0, 0, 0, 0} - }; - - sqlite4_backup *p = (sqlite4_backup *)clientData; - int iCmd; - int rc; - - rc = Tcl_GetIndexFromObjStruct( - interp, objv[1], aSub, sizeof(aSub[0]), "option", 0, &iCmd - ); - if( rc!=TCL_OK ){ - return rc; - } - if( objc!=(2 + aSub[iCmd].nArg) ){ - Tcl_WrongNumArgs(interp, 2, objv, aSub[iCmd].zArg); - return TCL_ERROR; - } - - switch( aSub[iCmd].eCmd ){ - - case BACKUP_FINISH: { - const char *zCmdName; - Tcl_CmdInfo cmdInfo; - zCmdName = Tcl_GetString(objv[0]); - Tcl_GetCommandInfo(interp, zCmdName, &cmdInfo); - cmdInfo.deleteProc = 0; - Tcl_SetCommandInfo(interp, zCmdName, &cmdInfo); - Tcl_DeleteCommand(interp, zCmdName); - - rc = sqlite4_backup_finish(p); - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC); - break; - } - - case BACKUP_STEP: { - int nPage; - if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &nPage) ){ - return TCL_ERROR; - } - rc = sqlite4_backup_step(p, nPage); - Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC); - break; - } - - case BACKUP_REMAINING: - Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_backup_remaining(p))); - break; - - case BACKUP_PAGECOUNT: - Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_backup_pagecount(p))); - break; - } - - return TCL_OK; -} - -static void backupTestFinish(ClientData clientData){ - sqlite4_backup *pBackup = (sqlite4_backup *)clientData; - sqlite4_backup_finish(pBackup); -} - -/* -** sqlite4_backup CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME -** -*/ -static int backupTestInit( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const*objv -){ - sqlite4_backup *pBackup; - sqlite4 *pDestDb; - sqlite4 *pSrcDb; - const char *zDestName; - const char *zSrcName; - const char *zCmd; - - if( objc!=6 ){ - Tcl_WrongNumArgs( - interp, 1, objv, "CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME" - ); - return TCL_ERROR; - } - - zCmd = Tcl_GetString(objv[1]); - getDbPointer(interp, Tcl_GetString(objv[2]), &pDestDb); - zDestName = Tcl_GetString(objv[3]); - getDbPointer(interp, Tcl_GetString(objv[4]), &pSrcDb); - zSrcName = Tcl_GetString(objv[5]); - - pBackup = sqlite4_backup_init(pDestDb, zDestName, pSrcDb, zSrcName); - if( !pBackup ){ - Tcl_AppendResult(interp, "sqlite4_backup_init() failed", 0); - return TCL_ERROR; - } - - Tcl_CreateObjCommand(interp, zCmd, backupTestCmd, pBackup, backupTestFinish); - Tcl_SetObjResult(interp, objv[1]); - return TCL_OK; -} - -int Sqlitetestbackup_Init(Tcl_Interp *interp){ - Tcl_CreateObjCommand(interp, "sqlite4_backup", backupTestInit, 0, 0); - return TCL_OK; -} DELETED src/test_btree.c Index: src/test_btree.c ================================================================== --- src/test_btree.c +++ /dev/null @@ -1,62 +0,0 @@ -/* -** 2007 May 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Code for testing the btree.c module in SQLite. This code -** is not included in the SQLite library. It is used for automated -** testing of the SQLite library. -*/ -#include "btreeInt.h" -#include - -/* -** Usage: sqlite4_shared_cache_report -** -** Return a list of file that are shared and the number of -** references to each file. -*/ -int sqlite4BtreeSharedCacheReport( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ -#ifndef SQLITE_OMIT_SHARED_CACHE - extern BtShared *sqlite4SharedCacheList; - BtShared *pBt; - Tcl_Obj *pRet = Tcl_NewObj(); - for(pBt=GLOBAL(BtShared*,sqlite4SharedCacheList); pBt; pBt=pBt->pNext){ - const char *zFile = sqlite4PagerFilename(pBt->pPager); - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1)); - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef)); - } - Tcl_SetObjResult(interp, pRet); -#endif - return TCL_OK; -} - -/* -** Print debugging information about all cursors to standard output. -*/ -void sqlite4BtreeCursorList(Btree *p){ -#ifdef SQLITE_DEBUG - BtCursor *pCur; - BtShared *pBt = p->pBt; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - MemPage *pPage = pCur->apPage[pCur->iPage]; - char *zMode = pCur->wrFlag ? "rw" : "ro"; - sqlite4DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", - pCur, pCur->pgnoRoot, zMode, - pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage], - (pCur->eState==CURSOR_VALID) ? "" : " eof" - ); - } -#endif -} Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -317,16 +317,10 @@ Tcl_SetVar2(interp, "sqlite_options", "icu", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "icu", "0", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_OMIT_INCRBLOB - Tcl_SetVar2(interp, "sqlite_options", "incrblob", "0", TCL_GLOBAL_ONLY); -#else - Tcl_SetVar2(interp, "sqlite_options", "incrblob", "1", TCL_GLOBAL_ONLY); -#endif /* SQLITE_OMIT_AUTOVACUUM */ - #ifdef SQLITE_OMIT_INTEGRITY_CHECK Tcl_SetVar2(interp, "sqlite_options", "integrityck", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "integrityck", "1", TCL_GLOBAL_ONLY); #endif DELETED src/test_loadext.c Index: src/test_loadext.c ================================================================== --- src/test_loadext.c +++ /dev/null @@ -1,122 +0,0 @@ -/* -** 2006 June 14 -** -** 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. -** -************************************************************************* -** Test extension for testing the sqlite4_load_extension() function. -*/ -#include -#include "sqlite4ext.h" -SQLITE_EXTENSION_INIT1 - -/* -** The half() SQL function returns half of its input value. -*/ -static void halfFunc( - sqlite4_context *context, - int argc, - sqlite4_value **argv -){ - sqlite4_result_double(context, 0.5*sqlite4_value_double(argv[0])); -} - -/* -** SQL functions to call the sqlite4_status function and return results. -*/ -static void statusFunc( - sqlite4_context *context, - int argc, - sqlite4_value **argv -){ - int op, mx, cur, resetFlag, rc; - if( sqlite4_value_type(argv[0])==SQLITE_INTEGER ){ - op = sqlite4_value_int(argv[0]); - }else if( sqlite4_value_type(argv[0])==SQLITE_TEXT ){ - int i; - const char *zName; - static const struct { - const char *zName; - int op; - } aOp[] = { - { "MEMORY_USED", SQLITE_STATUS_MEMORY_USED }, - { "PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED }, - { "PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW }, - { "SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED }, - { "SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW }, - { "MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE }, - }; - int nOp = sizeof(aOp)/sizeof(aOp[0]); - zName = (const char*)sqlite4_value_text(argv[0]); - for(i=0; i=nOp ){ - char *zMsg = sqlite4_mprintf("unknown status property: %s", zName); - sqlite4_result_error(context, zMsg, -1); - sqlite4_free(zMsg); - return; - } - }else{ - sqlite4_result_error(context, "unknown status type", -1); - return; - } - if( argc==2 ){ - resetFlag = sqlite4_value_int(argv[1]); - }else{ - resetFlag = 0; - } - rc = sqlite4_status(op, &cur, &mx, resetFlag); - if( rc!=SQLITE_OK ){ - char *zMsg = sqlite4_mprintf("sqlite4_status(%d,...) returns %d", op, rc); - sqlite4_result_error(context, zMsg, -1); - sqlite4_free(zMsg); - return; - } - if( argc==2 ){ - sqlite4_result_int(context, mx); - }else{ - sqlite4_result_int(context, cur); - } -} - -/* -** Extension load function. -*/ -int testloadext_init( - sqlite4 *db, - char **pzErrMsg, - const sqlite4_api_routines *pApi -){ - int nErr = 0; - SQLITE_EXTENSION_INIT2(pApi); - nErr |= sqlite4_create_function(db, "half", 1, SQLITE_ANY, 0, halfFunc, 0, 0); - nErr |= sqlite4_create_function(db, "sqlite4_status", 1, SQLITE_ANY, 0, - statusFunc, 0, 0); - nErr |= sqlite4_create_function(db, "sqlite4_status", 2, SQLITE_ANY, 0, - statusFunc, 0, 0); - return nErr ? SQLITE_ERROR : SQLITE_OK; -} - -/* -** Another extension entry point. This one always fails. -*/ -int testbrokenext_init( - sqlite4 *db, - char **pzErrMsg, - const sqlite4_api_routines *pApi -){ - char *zErr; - SQLITE_EXTENSION_INIT2(pApi); - zErr = sqlite4_mprintf("broken!"); - *pzErrMsg = zErr; - return 1; -} Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -994,12 +994,10 @@ if( discardChance<0 || discardChance>100 ){ Tcl_AppendResult(interp, "discard-chance should be between 0 and 100", (char*)0); return TCL_ERROR; } - installTestPCache(installFlag, (unsigned)discardChance, (unsigned)prngSeed, - (unsigned)highStress); return TCL_OK; } /* ** Usage: sqlite4_config_memstatus BOOLEAN @@ -1399,29 +1397,10 @@ #endif Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } -static int test_vfs_oom_test( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - extern int sqlite4_memdebug_vfs_oom_test; - if( objc>2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "?INTEGER?"); - return TCL_ERROR; - }else if( objc==2 ){ - int iNew; - if( Tcl_GetIntFromObj(interp, objv[1], &iNew) ) return TCL_ERROR; - sqlite4_memdebug_vfs_oom_test = iNew; - } - Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_memdebug_vfs_oom_test)); - return TCL_OK; -} - /* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ static struct { @@ -1456,15 +1435,14 @@ { "sqlite4_config_uri", test_config_uri ,0 }, { "sqlite4_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite4_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite4_dump_memsys5", test_dump_memsys3 ,5 }, { "sqlite4_install_memsys3", test_install_memsys3 ,0 }, - { "sqlite4_memdebug_vfs_oom_test", test_vfs_oom_test ,0 }, }; int i; for(i=0; i #include #include DELETED src/test_server.c Index: src/test_server.c ================================================================== --- src/test_server.c +++ /dev/null @@ -1,490 +0,0 @@ -/* -** 2006 January 07 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains demonstration code. Nothing in this file gets compiled -** or linked into the SQLite library unless you use a non-standard option: -** -** -DSQLITE_SERVER=1 -** -** The configure script will never generate a Makefile with the option -** above. You will need to manually modify the Makefile if you want to -** include any of the code from this file in your project. Or, at your -** option, you may copy and paste the code from this file and -** thereby avoiding a recompile of SQLite. -** -** -** This source file demonstrates how to use SQLite to create an SQL database -** server thread in a multiple-threaded program. One or more client threads -** send messages to the server thread and the server thread processes those -** messages in the order received and returns the results to the client. -** -** One might ask: "Why bother? Why not just let each thread connect -** to the database directly?" There are a several of reasons to -** prefer the client/server approach. -** -** (1) Some systems (ex: Redhat9) have broken threading implementations -** that prevent SQLite database connections from being used in -** a thread different from the one where they were created. With -** the client/server approach, all database connections are created -** and used within the server thread. Client calls to the database -** can be made from multiple threads (though not at the same time!) -** -** (2) Beginning with SQLite version 3.3.0, when two or more -** connections to the same database occur within the same thread, -** they can optionally share their database cache. This reduces -** I/O and memory requirements. Cache shared is controlled using -** the sqlite4_enable_shared_cache() API. -** -** (3) Database connections on a shared cache use table-level locking -** instead of file-level locking for improved concurrency. -** -** (4) Database connections on a shared cache can by optionally -** set to READ UNCOMMITTED isolation. (The default isolation for -** SQLite is SERIALIZABLE.) When this occurs, readers will -** never be blocked by a writer and writers will not be -** blocked by readers. There can still only be a single writer -** at a time, but multiple readers can simultaneously exist with -** that writer. This is a huge increase in concurrency. -** -** To summarize the rational for using a client/server approach: prior -** to SQLite version 3.3.0 it probably was not worth the trouble. But -** with SQLite version 3.3.0 and beyond you can get significant performance -** and concurrency improvements and memory usage reductions by going -** client/server. -** -** Note: The extra features of version 3.3.0 described by points (2) -** through (4) above are only available if you compile without the -** option -DSQLITE_OMIT_SHARED_CACHE. -** -** Here is how the client/server approach works: The database server -** thread is started on this procedure: -** -** void *sqlite4_server(void *NotUsed); -** -** The sqlite_server procedure runs as long as the g.serverHalt variable -** is false. A mutex is used to make sure no more than one server runs -** at a time. The server waits for messages to arrive on a message -** queue and processes the messages in order. -** -** Two convenience routines are provided for starting and stopping the -** server thread: -** -** void sqlite4_server_start(void); -** void sqlite4_server_stop(void); -** -** Both of the convenience routines return immediately. Neither will -** ever give an error. If a server is already started or already halted, -** then the routines are effectively no-ops. -** -** Clients use the following interfaces: -** -** sqlite4_client_open -** sqlite4_client_prepare -** sqlite4_client_step -** sqlite4_client_reset -** sqlite4_client_finalize -** sqlite4_client_close -** -** These interfaces work exactly like the standard core SQLite interfaces -** having the same names without the "_client_" infix. Many other SQLite -** interfaces can be used directly without having to send messages to the -** server as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined. -** The following interfaces fall into this second category: -** -** sqlite4_bind_* -** sqlite4_changes -** sqlite4_clear_bindings -** sqlite4_column_* -** sqlite4_complete -** sqlite4_create_collation -** sqlite4_create_function -** sqlite4_data_count -** sqlite4_db_handle -** sqlite4_errcode -** sqlite4_errmsg -** sqlite4_last_insert_rowid -** sqlite4_total_changes -** sqlite4_transfer_bindings -** -** A single SQLite connection (an sqlite4* object) or an SQLite statement -** (an sqlite4_stmt* object) should only be passed to a single interface -** function at a time. The connections and statements can be passed from -** any thread to any of the functions listed in the second group above as -** long as the same connection is not in use by two threads at once and -** as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined. Additional -** information about the SQLITE_ENABLE_MEMORY_MANAGEMENT constraint is -** below. -** -** The busy handler for all database connections should remain turned -** off. That means that any lock contention will cause the associated -** sqlite4_client_step() call to return immediately with an SQLITE_BUSY -** error code. If a busy handler is enabled and lock contention occurs, -** then the entire server thread will block. This will cause not only -** the requesting client to block but every other database client as -** well. It is possible to enhance the code below so that lock -** contention will cause the message to be placed back on the top of -** the queue to be tried again later. But such enhanced processing is -** not included here, in order to keep the example simple. -** -** This example code assumes the use of pthreads. Pthreads -** implementations are available for windows. (See, for example -** http://sourceware.org/pthreads-win32/announcement.html.) Or, you -** can translate the locking and thread synchronization code to use -** windows primitives easily enough. The details are left as an -** exercise to the reader. -** -**** Restrictions Associated With SQLITE_ENABLE_MEMORY_MANAGEMENT **** -** -** If you compile with SQLITE_ENABLE_MEMORY_MANAGEMENT defined, then -** SQLite includes code that tracks how much memory is being used by -** each thread. These memory counts can become confused if memory -** is allocated by one thread and then freed by another. For that -** reason, when SQLITE_ENABLE_MEMORY_MANAGEMENT is used, all operations -** that might allocate or free memory should be performanced in the same -** thread that originally created the database connection. In that case, -** many of the operations that are listed above as safe to be performed -** in separate threads would need to be sent over to the server to be -** done there. If SQLITE_ENABLE_MEMORY_MANAGEMENT is defined, then -** the following functions can be used safely from different threads -** without messing up the allocation counts: -** -** sqlite4_bind_parameter_name -** sqlite4_bind_parameter_index -** sqlite4_changes -** sqlite4_column_blob -** sqlite4_column_count -** sqlite4_complete -** sqlite4_data_count -** sqlite4_db_handle -** sqlite4_errcode -** sqlite4_errmsg -** sqlite4_last_insert_rowid -** sqlite4_total_changes -** -** The remaining functions are not thread-safe when memory management -** is enabled. So one would have to define some new interface routines -** along the following lines: -** -** sqlite4_client_bind_* -** sqlite4_client_clear_bindings -** sqlite4_client_column_* -** sqlite4_client_create_collation -** sqlite4_client_create_function -** sqlite4_client_transfer_bindings -** -** The example code in this file is intended for use with memory -** management turned off. So the implementation of these additional -** client interfaces is left as an exercise to the reader. -** -** It may seem surprising to the reader that the list of safe functions -** above does not include things like sqlite4_bind_int() or -** sqlite4_column_int(). But those routines might, in fact, allocate -** or deallocate memory. In the case of sqlite4_bind_int(), if the -** parameter was previously bound to a string that string might need -** to be deallocated before the new integer value is inserted. In -** the case of sqlite4_column_int(), the value of the column might be -** a UTF-16 string which will need to be converted to UTF-8 then into -** an integer. -*/ - -/* Include this to get the definition of SQLITE_THREADSAFE, in the -** case that default values are used. -*/ -#include "sqliteInt.h" - -/* -** Only compile the code in this file on UNIX with a SQLITE_THREADSAFE build -** and only if the SQLITE_SERVER macro is defined. -*/ -#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) -#if SQLITE_OS_UNIX && SQLITE_THREADSAFE - -/* -** We require only pthreads and the public interface of SQLite. -*/ -#include -#include "sqlite4.h" - -/* -** Messages are passed from client to server and back again as -** instances of the following structure. -*/ -typedef struct SqlMessage SqlMessage; -struct SqlMessage { - int op; /* Opcode for the message */ - sqlite4 *pDb; /* The SQLite connection */ - sqlite4_stmt *pStmt; /* A specific statement */ - int errCode; /* Error code returned */ - const char *zIn; /* Input filename or SQL statement */ - int nByte; /* Size of the zIn parameter for prepare() */ - const char *zOut; /* Tail of the SQL statement */ - SqlMessage *pNext; /* Next message in the queue */ - SqlMessage *pPrev; /* Previous message in the queue */ - pthread_mutex_t clientMutex; /* Hold this mutex to access the message */ - pthread_cond_t clientWakeup; /* Signal to wake up the client */ -}; - -/* -** Legal values for SqlMessage.op -*/ -#define MSG_Open 1 /* sqlite4_open(zIn, &pDb) */ -#define MSG_Prepare 2 /* sqlite4_prepare(pDb, zIn, nByte, &pStmt, &zOut) */ -#define MSG_Step 3 /* sqlite4_step(pStmt) */ -#define MSG_Reset 4 /* sqlite4_reset(pStmt) */ -#define MSG_Finalize 5 /* sqlite4_finalize(pStmt) */ -#define MSG_Close 6 /* sqlite4_close(pDb) */ -#define MSG_Done 7 /* Server has finished with this message */ - - -/* -** State information about the server is stored in a static variable -** named "g" as follows: -*/ -static struct ServerState { - pthread_mutex_t queueMutex; /* Hold this mutex to access the msg queue */ - pthread_mutex_t serverMutex; /* Held by the server while it is running */ - pthread_cond_t serverWakeup; /* Signal this condvar to wake up the server */ - volatile int serverHalt; /* Server halts itself when true */ - SqlMessage *pQueueHead; /* Head of the message queue */ - SqlMessage *pQueueTail; /* Tail of the message queue */ -} g = { - PTHREAD_MUTEX_INITIALIZER, - PTHREAD_MUTEX_INITIALIZER, - PTHREAD_COND_INITIALIZER, -}; - -/* -** Send a message to the server. Block until we get a reply. -** -** The mutex and condition variable in the message are uninitialized -** when this routine is called. This routine takes care of -** initializing them and destroying them when it has finished. -*/ -static void sendToServer(SqlMessage *pMsg){ - /* Initialize the mutex and condition variable on the message - */ - pthread_mutex_init(&pMsg->clientMutex, 0); - pthread_cond_init(&pMsg->clientWakeup, 0); - - /* Add the message to the head of the server's message queue. - */ - pthread_mutex_lock(&g.queueMutex); - pMsg->pNext = g.pQueueHead; - if( g.pQueueHead==0 ){ - g.pQueueTail = pMsg; - }else{ - g.pQueueHead->pPrev = pMsg; - } - pMsg->pPrev = 0; - g.pQueueHead = pMsg; - pthread_mutex_unlock(&g.queueMutex); - - /* Signal the server that the new message has be queued, then - ** block waiting for the server to process the message. - */ - pthread_mutex_lock(&pMsg->clientMutex); - pthread_cond_signal(&g.serverWakeup); - while( pMsg->op!=MSG_Done ){ - pthread_cond_wait(&pMsg->clientWakeup, &pMsg->clientMutex); - } - pthread_mutex_unlock(&pMsg->clientMutex); - - /* Destroy the mutex and condition variable of the message. - */ - pthread_mutex_destroy(&pMsg->clientMutex); - pthread_cond_destroy(&pMsg->clientWakeup); -} - -/* -** The following 6 routines are client-side implementations of the -** core SQLite interfaces: -** -** sqlite4_open -** sqlite4_prepare -** sqlite4_step -** sqlite4_reset -** sqlite4_finalize -** sqlite4_close -** -** Clients should use the following client-side routines instead of -** the core routines above. -** -** sqlite4_client_open -** sqlite4_client_prepare -** sqlite4_client_step -** sqlite4_client_reset -** sqlite4_client_finalize -** sqlite4_client_close -** -** Each of these routines creates a message for the desired operation, -** sends that message to the server, waits for the server to process -** then message and return a response. -*/ -int sqlite4_client_open(const char *zDatabaseName, sqlite4 **ppDb){ - SqlMessage msg; - msg.op = MSG_Open; - msg.zIn = zDatabaseName; - sendToServer(&msg); - *ppDb = msg.pDb; - return msg.errCode; -} -int sqlite4_client_prepare( - sqlite4 *pDb, - const char *zSql, - int nByte, - sqlite4_stmt **ppStmt, - const char **pzTail -){ - SqlMessage msg; - msg.op = MSG_Prepare; - msg.pDb = pDb; - msg.zIn = zSql; - msg.nByte = nByte; - sendToServer(&msg); - *ppStmt = msg.pStmt; - if( pzTail ) *pzTail = msg.zOut; - return msg.errCode; -} -int sqlite4_client_step(sqlite4_stmt *pStmt){ - SqlMessage msg; - msg.op = MSG_Step; - msg.pStmt = pStmt; - sendToServer(&msg); - return msg.errCode; -} -int sqlite4_client_reset(sqlite4_stmt *pStmt){ - SqlMessage msg; - msg.op = MSG_Reset; - msg.pStmt = pStmt; - sendToServer(&msg); - return msg.errCode; -} -int sqlite4_client_finalize(sqlite4_stmt *pStmt){ - SqlMessage msg; - msg.op = MSG_Finalize; - msg.pStmt = pStmt; - sendToServer(&msg); - return msg.errCode; -} -int sqlite4_client_close(sqlite4 *pDb){ - SqlMessage msg; - msg.op = MSG_Close; - msg.pDb = pDb; - sendToServer(&msg); - return msg.errCode; -} - -/* -** This routine implements the server. To start the server, first -** make sure g.serverHalt is false, then create a new detached thread -** on this procedure. See the sqlite4_server_start() routine below -** for an example. This procedure loops until g.serverHalt becomes -** true. -*/ -void *sqlite4_server(void *NotUsed){ - if( pthread_mutex_trylock(&g.serverMutex) ){ - return 0; /* Another server is already running */ - } - sqlite4_enable_shared_cache(1); - while( !g.serverHalt ){ - SqlMessage *pMsg; - - /* Remove the last message from the message queue. - */ - pthread_mutex_lock(&g.queueMutex); - while( g.pQueueTail==0 && g.serverHalt==0 ){ - pthread_cond_wait(&g.serverWakeup, &g.queueMutex); - } - pMsg = g.pQueueTail; - if( pMsg ){ - if( pMsg->pPrev ){ - pMsg->pPrev->pNext = 0; - }else{ - g.pQueueHead = 0; - } - g.pQueueTail = pMsg->pPrev; - } - pthread_mutex_unlock(&g.queueMutex); - if( pMsg==0 ) break; - - /* Process the message just removed - */ - pthread_mutex_lock(&pMsg->clientMutex); - switch( pMsg->op ){ - case MSG_Open: { - pMsg->errCode = sqlite4_open(pMsg->zIn, &pMsg->pDb); - break; - } - case MSG_Prepare: { - pMsg->errCode = sqlite4_prepare(pMsg->pDb, pMsg->zIn, pMsg->nByte, - &pMsg->pStmt, &pMsg->zOut); - break; - } - case MSG_Step: { - pMsg->errCode = sqlite4_step(pMsg->pStmt); - break; - } - case MSG_Reset: { - pMsg->errCode = sqlite4_reset(pMsg->pStmt); - break; - } - case MSG_Finalize: { - pMsg->errCode = sqlite4_finalize(pMsg->pStmt); - break; - } - case MSG_Close: { - pMsg->errCode = sqlite4_close(pMsg->pDb); - break; - } - } - - /* Signal the client that the message has been processed. - */ - pMsg->op = MSG_Done; - pthread_mutex_unlock(&pMsg->clientMutex); - pthread_cond_signal(&pMsg->clientWakeup); - } - pthread_mutex_unlock(&g.serverMutex); - return 0; -} - -/* -** Start a server thread if one is not already running. If there -** is aleady a server thread running, the new thread will quickly -** die and this routine is effectively a no-op. -*/ -void sqlite4_server_start(void){ - pthread_t x; - int rc; - g.serverHalt = 0; - rc = pthread_create(&x, 0, sqlite4_server, 0); - if( rc==0 ){ - pthread_detach(x); - } -} - -/* -** If a server thread is running, then stop it. If no server is -** running, this routine is effectively a no-op. -** -** This routine waits until the server has actually stopped before -** returning. -*/ -void sqlite4_server_stop(void){ - g.serverHalt = 1; - pthread_cond_broadcast(&g.serverWakeup); - pthread_mutex_lock(&g.serverMutex); - pthread_mutex_unlock(&g.serverMutex); -} - -#endif /* SQLITE_OS_UNIX && SQLITE_THREADSAFE */ -#endif /* defined(SQLITE_SERVER) */ Index: src/test_storage.c ================================================================== --- src/test_storage.c +++ src/test_storage.c @@ -27,11 +27,11 @@ extern const char *sqlite4TestErrorName(int); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); } /* -** TCLCMD: storage_open URI +** TCLCMD: storage_open URI FLAGS ** ** Return a string that identifies the new storage object. */ static int test_storage_open( void * clientData, @@ -39,16 +39,20 @@ int objc, Tcl_Obj *CONST objv[] ){ KVStore *pNew = 0; int rc; + int flags; + sqlite4 db; char zRes[50]; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "URI"); + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "URI FLAGS"); return TCL_ERROR; } - rc = sqlite4KVStoreOpen(Tcl_GetString(objv[1]), &pNew); + if( Tcl_GetIntFromObj(interp, objv[2], &flags) ) return TCL_ERROR; + memset(&db, 0, sizeof(db)); + rc = sqlite4KVStoreOpen(&db, "test", Tcl_GetString(objv[1]), &pNew, flags); if( rc ){ sqlite4KVStoreClose(pNew); storageSetTclErrorName(interp, rc); return TCL_ERROR; } @@ -225,11 +229,16 @@ Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); return TCL_ERROR; } p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; - rc = sqlite4KVStoreCommit(p, iLevel); + rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); if( rc ){ storageSetTclErrorName(interp, rc); return TCL_ERROR; } return TCL_OK; DELETED src/test_superlock.c Index: src/test_superlock.c ================================================================== --- src/test_superlock.c +++ /dev/null @@ -1,356 +0,0 @@ -/* -** 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: -** -** sqlite4demo_superlock() -** sqlite4demo_superunlock() -*/ - -#include -#include /* memset(), strlen() */ -#include /* 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 sqlite4demo_superlock() is -** actually a pointer to an instance of this structure. -*/ -struct Superlock { - sqlite4 *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 */ - sqlite4_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */ - - rc = sqlite4_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - - pLock->bWal = 0; - if( SQLITE_ROW==sqlite4_step(pStmt) ){ - const char *zMode = (const char *)sqlite4_column_text(pStmt, 0); - if( zMode && strlen(zMode)==3 && sqlite4_strnicmp("wal", zMode, 3)==0 ){ - pLock->bWal = 1; - } - } - - return sqlite4_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( - sqlite4_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)(sqlite4_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( - sqlite4 *db, /* Database handle open on WAL database */ - SuperlockBusy *pBusy /* Busy handler wrapper object */ -){ - int rc; /* Return code */ - sqlite4_file *fd = 0; /* Main database file handle */ - void volatile *p = 0; /* Pointer to first page of shared memory */ - - /* Obtain a pointer to the sqlite4_file object open on the main db file. */ - rc = sqlite4_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 -** sqlite4demo_superlock(). -*/ -void sqlite4demo_superunlock(void *pLock){ - Superlock *p = (Superlock *)pLock; - if( p->bWal ){ - int rc; /* Return code */ - int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE; - sqlite4_file *fd = 0; - rc = sqlite4_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); - } - } - sqlite4_close(p->db); - sqlite4_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 sqlite4demo_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 sqlite4_busy_handler()) -** until either the lock can be obtained or the busy-handler function returns -** 0 (indicating "give up"). -*/ -int sqlite4demo_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 = sqlite4_malloc(sizeof(Superlock)); - if( !pLock ) return SQLITE_NOMEM; - memset(pLock, 0, sizeof(Superlock)); - - /* Open a database handle on the file to superlock. */ - rc = sqlite4_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 sqlite4demo_superlock(), including any made during the - ** "BEGIN EXCLUSIVE". - */ - if( rc==SQLITE_OK ){ - busy.xBusy = xBusy; - busy.pBusyArg = pBusyArg; - sqlite4_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy); - rc = sqlite4_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 = sqlite4_exec(pLock->db, "COMMIT", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = superlockWalLock(pLock->db, &busy); - } - } - } - - if( rc!=SQLITE_OK ){ - sqlite4demo_superunlock(pLock); - *ppLock = 0; - }else{ - *ppLock = pLock; - } - - return rc; -} - -/* -** End of example code. Everything below here is the test harness. -************************************************************************** -************************************************************************** -*************************************************************************/ - - -#ifdef SQLITE_TEST - -#include - -struct InterpAndScript { - Tcl_Interp *interp; - Tcl_Obj *pScript; -}; -typedef struct InterpAndScript InterpAndScript; - -static void superunlock_del(ClientData cd){ - sqlite4demo_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: sqlite4demo_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 sqlite4demo_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 = sqlite4demo_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 *sqlite4ErrStr(int); - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, sqlite4ErrStr(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, "sqlite4demo_superlock", superlock_cmd, 0, 0); - return TCL_OK; -} -#endif DELETED src/test_syscall.c Index: src/test_syscall.c ================================================================== --- src/test_syscall.c +++ /dev/null @@ -1,674 +0,0 @@ -/* -** 2011 March 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. -** -************************************************************************* -** -** The code in this file implements a Tcl interface used to test error -** handling in the os_unix.c module. Wrapper functions that support fault -** injection are registered as the low-level OS functions using the -** xSetSystemCall() method of the VFS. The Tcl interface is as follows: -** -** -** test_syscall install LIST -** Install wrapper functions for all system calls in argument LIST. -** LIST must be a list consisting of zero or more of the following -** literal values: -** -** open close access getcwd stat fstat -** ftruncate fcntl read pread pread64 write -** pwrite pwrite64 fchmod fallocate -** -** test_syscall uninstall -** Uninstall all wrapper functions. -** -** test_syscall fault ?COUNT PERSIST? -** If [test_syscall fault] is invoked without the two arguments, fault -** injection is disabled. Otherwise, fault injection is configured to -** cause a failure on the COUNT'th next call to a system call with a -** wrapper function installed. A COUNT value of 1 means fail the next -** system call. -** -** Argument PERSIST is interpreted as a boolean. If true, the all -** system calls following the initial failure also fail. Otherwise, only -** the single transient failure is injected. -** -** test_syscall errno CALL ERRNO -** Set the value that the global "errno" is set to following a fault -** in call CALL. Argument CALL must be one of the system call names -** listed above (under [test_syscall install]). ERRNO is a symbolic -** name (i.e. "EACCES"). Not all errno codes are supported. Add extra -** to the aErrno table in function test_syscall_errno() below as -** required. -** -** test_syscall reset ?SYSTEM-CALL? -** With no argument, this is an alias for the [uninstall] command. However, -** this command uses a VFS call of the form: -** -** xSetSystemCall(pVfs, 0, 0); -** -** To restore the default system calls. The [uninstall] command restores -** each system call individually by calling (i.e.): -** -** xSetSystemCall(pVfs, "open", 0); -** -** With an argument, this command attempts to reset the system call named -** by the parameter using the same method as [uninstall]. -** -** test_syscall exists SYSTEM-CALL -** Return true if the named system call exists. Or false otherwise. -** -** test_syscall list -** Return a list of all system calls. The list is constructed using -** the xNextSystemCall() VFS method. -*/ - -#include "sqlite4.h" -#include "tcl.h" -#include -#include -#include - -#include "sqliteInt.h" -#if SQLITE_OS_UNIX - -/* From test1.c */ -extern const char *sqlite4TestErrorName(int); - -#include -#include - -static struct TestSyscallGlobal { - int bPersist; /* 1 for persistent errors, 0 for transient */ - int nCount; /* Fail after this many more calls */ - int nFail; /* Number of failures that have occurred */ -} gSyscall = { 0, 0 }; - -static int ts_open(const char *, int, int); -static int ts_close(int fd); -static int ts_access(const char *zPath, int mode); -static char *ts_getcwd(char *zPath, size_t nPath); -static int ts_stat(const char *zPath, struct stat *p); -static int ts_fstat(int fd, struct stat *p); -static int ts_ftruncate(int fd, off_t n); -static int ts_fcntl(int fd, int cmd, ... ); -static int ts_read(int fd, void *aBuf, size_t nBuf); -static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off); -static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off); -static int ts_write(int fd, const void *aBuf, size_t nBuf); -static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off); -static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off); -static int ts_fchmod(int fd, mode_t mode); -static int ts_fallocate(int fd, off_t off, off_t len); - - -struct TestSyscallArray { - const char *zName; - sqlite4_syscall_ptr xTest; - sqlite4_syscall_ptr xOrig; - int default_errno; /* Default value for errno following errors */ - int custom_errno; /* Current value for errno if error */ -} aSyscall[] = { - /* 0 */ { "open", (sqlite4_syscall_ptr)ts_open, 0, EACCES, 0 }, - /* 1 */ { "close", (sqlite4_syscall_ptr)ts_close, 0, 0, 0 }, - /* 2 */ { "access", (sqlite4_syscall_ptr)ts_access, 0, 0, 0 }, - /* 3 */ { "getcwd", (sqlite4_syscall_ptr)ts_getcwd, 0, 0, 0 }, - /* 4 */ { "stat", (sqlite4_syscall_ptr)ts_stat, 0, 0, 0 }, - /* 5 */ { "fstat", (sqlite4_syscall_ptr)ts_fstat, 0, 0, 0 }, - /* 6 */ { "ftruncate", (sqlite4_syscall_ptr)ts_ftruncate, 0, EIO, 0 }, - /* 7 */ { "fcntl", (sqlite4_syscall_ptr)ts_fcntl, 0, EACCES, 0 }, - /* 8 */ { "read", (sqlite4_syscall_ptr)ts_read, 0, 0, 0 }, - /* 9 */ { "pread", (sqlite4_syscall_ptr)ts_pread, 0, 0, 0 }, - /* 10 */ { "pread64", (sqlite4_syscall_ptr)ts_pread64, 0, 0, 0 }, - /* 11 */ { "write", (sqlite4_syscall_ptr)ts_write, 0, 0, 0 }, - /* 12 */ { "pwrite", (sqlite4_syscall_ptr)ts_pwrite, 0, 0, 0 }, - /* 13 */ { "pwrite64", (sqlite4_syscall_ptr)ts_pwrite64, 0, 0, 0 }, - /* 14 */ { "fchmod", (sqlite4_syscall_ptr)ts_fchmod, 0, 0, 0 }, - /* 15 */ { "fallocate", (sqlite4_syscall_ptr)ts_fallocate, 0, 0, 0 }, - { 0, 0, 0, 0, 0 } -}; - -#define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig) -#define orig_close ((int(*)(int))aSyscall[1].xOrig) -#define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig) -#define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig) -#define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig) -#define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig) -#define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig) -#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig) -#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig) -#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig) -#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig) -#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig) -#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ - aSyscall[12].xOrig) -#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ - aSyscall[13].xOrig) -#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig) -#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig) - -/* -** This function is called exactly once from within each invocation of a -** system call wrapper in this file. It returns 1 if the function should -** fail, or 0 if it should succeed. -*/ -static int tsIsFail(void){ - gSyscall.nCount--; - if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){ - gSyscall.nFail++; - return 1; - } - return 0; -} - -/* -** Return the current error-number value for function zFunc. zFunc must be -** the name of a system call in the aSyscall[] table. -** -** Usually, the current error-number is the value that errno should be set -** to if the named system call fails. The exception is "fallocate". See -** comments above the implementation of ts_fallocate() for details. -*/ -static int tsErrno(const char *zFunc){ - int i; - int nFunc = strlen(zFunc); - for(i=0; aSyscall[i].zName; i++){ - if( strlen(aSyscall[i].zName)!=nFunc ) continue; - if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; - return aSyscall[i].custom_errno; - } - - assert(0); - return 0; -} - -/* -** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the -** value of errno before returning. -*/ -static int tsIsFailErrno(const char *zFunc){ - if( tsIsFail() ){ - errno = tsErrno(zFunc); - return 1; - } - return 0; -} - -/* -** A wrapper around open(). -*/ -static int ts_open(const char *zFile, int flags, int mode){ - if( tsIsFailErrno("open") ){ - return -1; - } - return orig_open(zFile, flags, mode); -} - -/* -** A wrapper around close(). -*/ -static int ts_close(int fd){ - if( tsIsFail() ){ - /* Even if simulating an error, close the original file-descriptor. - ** This is to stop the test process from running out of file-descriptors - ** when running a long test. If a call to close() appears to fail, SQLite - ** never attempts to use the file-descriptor afterwards (or even to close - ** it a second time). */ - orig_close(fd); - return -1; - } - return orig_close(fd); -} - -/* -** A wrapper around access(). -*/ -static int ts_access(const char *zPath, int mode){ - if( tsIsFail() ){ - return -1; - } - return orig_access(zPath, mode); -} - -/* -** A wrapper around getcwd(). -*/ -static char *ts_getcwd(char *zPath, size_t nPath){ - if( tsIsFail() ){ - return NULL; - } - return orig_getcwd(zPath, nPath); -} - -/* -** A wrapper around stat(). -*/ -static int ts_stat(const char *zPath, struct stat *p){ - if( tsIsFail() ){ - return -1; - } - return orig_stat(zPath, p); -} - -/* -** A wrapper around fstat(). -*/ -static int ts_fstat(int fd, struct stat *p){ - if( tsIsFailErrno("fstat") ){ - return -1; - } - return orig_fstat(fd, p); -} - -/* -** A wrapper around ftruncate(). -*/ -static int ts_ftruncate(int fd, off_t n){ - if( tsIsFailErrno("ftruncate") ){ - return -1; - } - return orig_ftruncate(fd, n); -} - -/* -** A wrapper around fcntl(). -*/ -static int ts_fcntl(int fd, int cmd, ... ){ - va_list ap; - void *pArg; - if( tsIsFailErrno("fcntl") ){ - return -1; - } - va_start(ap, cmd); - pArg = va_arg(ap, void *); - return orig_fcntl(fd, cmd, pArg); -} - -/* -** A wrapper around read(). -*/ -static int ts_read(int fd, void *aBuf, size_t nBuf){ - if( tsIsFailErrno("read") ){ - return -1; - } - return orig_read(fd, aBuf, nBuf); -} - -/* -** A wrapper around pread(). -*/ -static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){ - if( tsIsFailErrno("pread") ){ - return -1; - } - return orig_pread(fd, aBuf, nBuf, off); -} - -/* -** A wrapper around pread64(). -*/ -static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){ - if( tsIsFailErrno("pread64") ){ - return -1; - } - return orig_pread64(fd, aBuf, nBuf, off); -} - -/* -** A wrapper around write(). -*/ -static int ts_write(int fd, const void *aBuf, size_t nBuf){ - if( tsIsFailErrno("write") ){ - if( tsErrno("write")==EINTR ) orig_write(fd, aBuf, nBuf/2); - return -1; - } - return orig_write(fd, aBuf, nBuf); -} - -/* -** A wrapper around pwrite(). -*/ -static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){ - if( tsIsFailErrno("pwrite") ){ - return -1; - } - return orig_pwrite(fd, aBuf, nBuf, off); -} - -/* -** A wrapper around pwrite64(). -*/ -static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){ - if( tsIsFailErrno("pwrite64") ){ - return -1; - } - return orig_pwrite64(fd, aBuf, nBuf, off); -} - -/* -** A wrapper around fchmod(). -*/ -static int ts_fchmod(int fd, mode_t mode){ - if( tsIsFail() ){ - return -1; - } - return orig_fchmod(fd, mode); -} - -/* -** A wrapper around fallocate(). -** -** SQLite assumes that the fallocate() function is compatible with -** posix_fallocate(). According to the Linux man page (2009-09-30): -** -** posix_fallocate() returns zero on success, or an error number on -** failure. Note that errno is not set. -*/ -static int ts_fallocate(int fd, off_t off, off_t len){ - if( tsIsFail() ){ - return tsErrno("fallocate"); - } - return orig_fallocate(fd, off, len); -} - -static int test_syscall_install( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_vfs *pVfs; - int nElem; - int i; - Tcl_Obj **apElem; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST"); - return TCL_ERROR; - } - if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){ - return TCL_ERROR; - } - pVfs = sqlite4_vfs_find(0); - - for(i=0; ixGetSystemCall(pVfs, aSyscall[iCall].zName); - pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest); - } - aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno; - } - - return TCL_OK; -} - -static int test_syscall_uninstall( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_vfs *pVfs; - int i; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - pVfs = sqlite4_vfs_find(0); - for(i=0; aSyscall[i].zName; i++){ - if( aSyscall[i].xOrig ){ - pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0); - aSyscall[i].xOrig = 0; - } - } - return TCL_OK; -} - -static int test_syscall_reset( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_vfs *pVfs; - int i; - int rc; - - if( objc!=2 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - pVfs = sqlite4_vfs_find(0); - if( objc==2 ){ - rc = pVfs->xSetSystemCall(pVfs, 0, 0); - for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0; - }else{ - int nFunc; - char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc); - rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0); - for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){ - if( strlen(aSyscall[i].zName)!=nFunc ) continue; - if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; - aSyscall[i].xOrig = 0; - } - } - if( rc!=SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); - return TCL_ERROR; - } - - Tcl_ResetResult(interp); - return TCL_OK; -} - -static int test_syscall_exists( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_vfs *pVfs; - sqlite4_syscall_ptr x; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - pVfs = sqlite4_vfs_find(0); - x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2])); - - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0)); - return TCL_OK; -} - -static int test_syscall_fault( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int nCount = 0; - int bPersist = 0; - - if( objc!=2 && objc!=4 ){ - Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?"); - return TCL_ERROR; - } - - if( objc==4 ){ - if( Tcl_GetIntFromObj(interp, objv[2], &nCount) - || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist) - ){ - return TCL_ERROR; - } - } - - Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail)); - gSyscall.nCount = nCount; - gSyscall.bPersist = bPersist; - gSyscall.nFail = 0; - return TCL_OK; -} - -static int test_syscall_errno( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int iCall; - int iErrno; - int rc; - - struct Errno { - const char *z; - int i; - } aErrno[] = { - { "EACCES", EACCES }, - { "EINTR", EINTR }, - { "EIO", EIO }, - { "EOVERFLOW", EOVERFLOW }, - { "ENOMEM", ENOMEM }, - { "EAGAIN", EAGAIN }, - { "ETIMEDOUT", ETIMEDOUT }, - { "EBUSY", EBUSY }, - { "EPERM", EPERM }, - { "EDEADLK", EDEADLK }, - { "ENOLCK", ENOLCK }, - { 0, 0 } - }; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO"); - return TCL_ERROR; - } - - rc = Tcl_GetIndexFromObjStruct(interp, - objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall - ); - if( rc!=TCL_OK ) return rc; - rc = Tcl_GetIndexFromObjStruct(interp, - objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno - ); - if( rc!=TCL_OK ) return rc; - - aSyscall[iCall].custom_errno = aErrno[iErrno].i; - return TCL_OK; -} - -static int test_syscall_list( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zSys; - sqlite4_vfs *pVfs; - Tcl_Obj *pList; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - pVfs = sqlite4_vfs_find(0); - pList = Tcl_NewObj(); - Tcl_IncrRefCount(pList); - for(zSys = pVfs->xNextSystemCall(pVfs, 0); - zSys!=0; - zSys = pVfs->xNextSystemCall(pVfs, zSys) - ){ - Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1)); - } - - Tcl_SetObjResult(interp, pList); - Tcl_DecrRefCount(pList); - return TCL_OK; -} - -static int test_syscall_defaultvfs( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite4_vfs *pVfs; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - pVfs = sqlite4_vfs_find(0); - Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1)); - return TCL_OK; -} - -static int test_syscall( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - struct SyscallCmd { - const char *zName; - Tcl_ObjCmdProc *xCmd; - } aCmd[] = { - { "fault", test_syscall_fault }, - { "install", test_syscall_install }, - { "uninstall", test_syscall_uninstall }, - { "reset", test_syscall_reset }, - { "errno", test_syscall_errno }, - { "exists", test_syscall_exists }, - { "list", test_syscall_list }, - { "defaultvfs", test_syscall_defaultvfs }, - { 0, 0 } - }; - int iCmd; - int rc; - - if( objc<2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); - return TCL_ERROR; - } - rc = Tcl_GetIndexFromObjStruct(interp, - objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd - ); - if( rc!=TCL_OK ) return rc; - return aCmd[iCmd].xCmd(clientData, interp, objc, objv); -} - -int SqlitetestSyscall_Init(Tcl_Interp *interp){ - struct SyscallCmd { - const char *zName; - Tcl_ObjCmdProc *xCmd; - } aCmd[] = { - { "test_syscall", test_syscall}, - }; - int i; - - for(i=0; ipSchema ){ HashElem *p; - assert( sqlite4SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ Trigger *pTrig = (Trigger *)sqliteHashData(p); if( pTrig->pTabSchema==pTab->pSchema && 0==sqlite4StrICmp(pTrig->table, pTab->zName) ){ @@ -177,11 +176,10 @@ ** specified name exists */ zName = sqlite4NameFromToken(db, pName); if( !zName || SQLITE_OK!=sqlite4CheckObjectName(pParse, zName) ){ goto trigger_cleanup; } - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); if( sqlite4HashFind(&(db->aDb[iDb].pSchema->trigHash), zName, sqlite4Strlen30(zName)) ){ if( !noErr ){ sqlite4ErrorMsg(pParse, "trigger %T already exists", pName); }else{ @@ -319,11 +317,10 @@ } if( db->init.busy ){ Trigger *pLink = pTrig; Hash *pHash = &db->aDb[iDb].pSchema->trigHash; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); pTrig = sqlite4HashInsert(pHash, zName, sqlite4Strlen30(zName), pTrig); if( pTrig ){ db->mallocFailed = 1; }else if( pLink->pSchema==pLink->pTabSchema ){ Table *pTab; @@ -501,15 +498,13 @@ assert( pName->nSrc==1 ); zDb = pName->a[0].zDatabase; zName = pName->a[0].zName; nName = sqlite4Strlen30(zName); - assert( zDb!=0 || sqlite4BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite4StrICmp(db->aDb[j].zName, zDb) ) continue; - assert( sqlite4SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite4HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ @@ -599,11 +594,10 @@ */ void sqlite4UnlinkAndDeleteTrigger(sqlite4 *db, int iDb, const char *zName){ Trigger *pTrigger; Hash *pHash; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); pHash = &(db->aDb[iDb].pSchema->trigHash); pTrigger = sqlite4HashInsert(pHash, zName, sqlite4Strlen30(zName), 0); if( ALWAYS(pTrigger) ){ if( pTrigger->pSchema==pTrigger->pTabSchema ){ Table *pTab = tableOfTrigger(pTrigger); Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -642,11 +642,10 @@ ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite4VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); - sqlite4VdbeChangeP5(v, BTREE_UNORDERED); /* fill the ephemeral table */ sqlite4SelectDestInit(&dest, SRT_Table, ephemTab); sqlite4Select(pParse, pSelect, &dest); Index: src/varint.c ================================================================== --- src/varint.c +++ src/varint.c @@ -94,23 +94,33 @@ ** */ #include "sqliteInt.h" /* -** Decode the varint in z[]. Write the integer value into *pResult and -** return the number of bytes in the varint. +** Decode the varint in the first n bytes z[]. Write the integer value +** into *pResult and return the number of bytes in the varint. +** +** If the decode fails because there are not enough bytes in z[] then +** return 0; */ -int sqlite4GetVarint64(const unsigned char *z, sqlite4_uint64 *pResult){ +int sqlite4GetVarint64( + const unsigned char *z, + int n, + sqlite4_uint64 *pResult +){ unsigned int x; + if( n<1 ) return 0; if( z[0]<=240 ){ *pResult = z[0]; return 1; } if( z[0]<=248 ){ + if( n<2 ) return 0; *pResult = (z[0]-241)*256 + z[1] + 240; return 2; } + if( n=1 && n1<=9 ); - n2 = sqlite4GetVarint64(z, &y); + n2 = sqlite4GetVarint64(z, n1, &y); assert( n1==n2 ); assert( x==y ); + n2 = sqlite4GetVarint64(z, n1-1, &y); + assert( n2==0 ); if( i>0 ){ int c = memcmp(z,zp,pnpx ){ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -189,11 +189,11 @@ static VdbeCursor *allocateCursor( Vdbe *p, /* The virtual machine */ int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ int iDb, /* Database the cursor belongs to, or -1 */ - int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ + int isTrueCursor /* True real cursor. False for pseudo-table or vtab */ ){ /* Find the memory cell that will be used to store the blob of memory ** required for this VdbeCursor structure. It is convenient to use a ** vdbe memory cell to manage the memory allocation required for a ** VdbeCursor structure for the following reasons: @@ -215,11 +215,10 @@ int nByte; VdbeCursor *pCx = 0; nByte = ROUND8(sizeof(VdbeCursor)) + - (isBtreeCursor?sqlite4BtreeCursorSize():0) + 2*nField*sizeof(u32); assert( iCurnCursor ); if( p->apCsr[iCur] ){ sqlite4VdbeFreeCursor(p, p->apCsr[iCur]); @@ -228,18 +227,10 @@ if( SQLITE_OK==sqlite4VdbeMemGrow(pMem, nByte, 0) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; memset(pCx, 0, sizeof(VdbeCursor)); pCx->iDb = iDb; pCx->nField = nField; - if( nField ){ - pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; - } - if( isBtreeCursor ){ - pCx->pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; - sqlite4BtreeCursorZero(pCx->pCursor); - } } return pCx; } /* @@ -476,31 +467,10 @@ ** flag on jump instructions, we get a (small) speed improvement. */ #define CHECK_FOR_INTERRUPT \ if( db->u1.isInterrupted ) goto abort_due_to_interrupt; - -#ifndef NDEBUG -/* -** This function is only called from within an assert() expression. It -** checks that the sqlite4.nTransaction variable is correctly set to -** the number of non-transaction savepoints currently in the -** linked list starting at sqlite4.pSavepoint. -** -** Usage: -** -** assert( checkSavepointCount(db) ); -*/ -static int checkSavepointCount(sqlite4 *db){ - int n = 0; - Savepoint *p; - for(p=db->pSavepoint; p; p=p->pNext) n++; - assert( n==(db->nSavepoint + db->isTransactionSavepoint) ); - return 1; -} -#endif - /* ** Transfer error message text from an sqlite4_vtab.zErrMsg (text stored ** in memory obtained from sqlite4_malloc) into a Vdbe.zErrMsg (text stored ** in memory obtained from sqlite4DbMalloc). */ @@ -571,11 +541,10 @@ int origPc; /* Program counter at start of opcode */ #endif /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite4_step() verifies this */ - sqlite4VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite4_column_text() or ** sqlite4_column_text16() failed. */ goto no_mem; } @@ -1114,11 +1083,11 @@ /* If this statement has violated immediate foreign key constraints, do ** not return the number of rows modified. And do not RELEASE the statement ** transaction. It needs to be rolled back. */ if( SQLITE_OK!=(rc = sqlite4VdbeCheckFk(p, 0)) ){ assert( db->flags&SQLITE_CountRows ); - assert( p->usesStmtJournal ); + assert( p->needSavepoint ); break; } /* If the SQLITE_CountRows flag is set in sqlite4.flags mask, then ** DML statements invoke this opcode to return the number of rows @@ -1133,11 +1102,10 @@ ** may lead to overlapping statement transactions. ** ** The statement transaction is never a top-level transaction. Hence ** the RELEASE call below can never fail. */ - assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); rc = sqlite4VdbeCloseStatement(p, SAVEPOINT_RELEASE); if( NEVER(rc!=SQLITE_OK) ){ break; } @@ -1819,12 +1787,10 @@ applyAffinity(pIn3, affinity, encoding); if( db->mallocFailed ) goto no_mem; } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - ExpandBlob(pIn1); - ExpandBlob(pIn3); res = sqlite4MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ case OP_Eq: res = res==0; break; case OP_Ne: res = res!=0; break; @@ -2120,296 +2086,57 @@ ** then the cache of the cursor is reset prior to extracting the column. ** The first OP_Column against a pseudo-table after the value of the content ** register has changed should have this bit set. */ case OP_Column: { - u32 payloadSize; /* Number of bytes in the record */ - i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ - int p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aType; /* aType[i] holds the numeric type of the i-th column */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ - Mem *pDest; /* Where to write the extracted value */ - Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ - u32 offset; /* Offset into the data */ - u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ - u32 t; /* A type code from the record header */ - Mem *pReg; /* PseudoTable input register */ - + KVCursor *pKVCur; /* Cursor for current entry in the KV storage */ + ValueDecoder *pCodec; /* The decoder object */ + int p1; /* Index of VdbeCursor to decode */ + VdbeCursor *pC; /* The VDBE cursor */ + Mem *pDest; /* Where to write the results */ + const KVByteArray *aData; /* The content to be decoded */ + KVSize nData; /* Size of aData[] in bytes */ + Mem *pDefault; /* Default value from P4 */ + Mem *pReg; /* */ p1 = pOp->p1; - p2 = pOp->p2; - pC = 0; - memset(&sMem, 0, sizeof(sMem)); assert( p1nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); - zRec = 0; - - /* This block sets the variable payloadSize to be the total number of - ** bytes in the record. - ** - ** zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables - ** If the record is stored in a cursor, the complete record text - ** might be available in the pC->aRow cache. Or it might not be. - ** If the data is unavailable, zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the VdbeCursor.nField element. - */ pC = p->apCsr[p1]; assert( pC!=0 ); #ifndef SQLITE_OMIT_VIRTUALTABLE assert( pC->pVtabCursor==0 ); #endif - pCrsr = pC->pCursor; - if( pCrsr!=0 ){ - /* The record is stored in a B-Tree */ - rc = sqlite4VdbeCursorMoveto(pC); - if( rc ) goto abort_due_to_error; + pKVCur = pC->pKVCur; + if( pKVCur!=0 ){ if( pC->nullRow ){ - payloadSize = 0; - }else if( pC->cacheStatus==p->cacheCtr ){ - payloadSize = pC->payloadSize; - zRec = (char*)pC->aRow; - }else if( pC->isIndex ){ - assert( sqlite4BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite4BtreeKeySize(pCrsr, &payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite4BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for payloadSize64 to be - ** larger than 32 bits. */ - assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); - payloadSize = (u32)payloadSize64; + aData = 0; }else{ - assert( sqlite4BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite4BtreeDataSize(pCrsr, &payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + rc = sqlite4KVCursorData(pKVCur, 0, -1, &aData, &nData); } }else if( ALWAYS(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; - assert( payloadSize==0 || zRec!=0 ); - }else{ - /* Consider the row to be NULL */ - payloadSize = 0; - } - - /* If payloadSize is 0, then just store a NULL. This can happen because of - ** nullRow or because of a corrupt database. */ - if( payloadSize==0 ){ - MemSetTypeFlag(pDest, MEM_Null); - goto op_column_out; - } - assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - nField = pC->nField; - assert( p2aType; - if( pC->cacheStatus==p->cacheCtr ){ - aOffset = pC->aOffset; - }else{ - assert(aType); - avail = 0; - pC->aOffset = aOffset = &aType[nField]; - pC->payloadSize = payloadSize; - pC->cacheStatus = p->cacheCtr; - - /* Figure out how many bytes are in the header */ - if( zRec ){ - zData = zRec; - }else{ - if( pC->isIndex ){ - zData = (char*)sqlite4BtreeKeyFetch(pCrsr, &avail); - }else{ - zData = (char*)sqlite4BtreeDataFetch(pCrsr, &avail); - } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - assert( avail>=0 ); - if( payloadSize <= (u32)avail ){ - zRec = zData; - pC->aRow = (u8*)zData; - }else{ - pC->aRow = 0; - } - } - /* The following assert is true in all cases accept when - ** the database file has been corrupted externally. - ** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */ - szHdr = getVarint32((u8*)zData, offset); - - /* Make sure a corrupt database has not given us an oversize header. - ** Do this now to avoid an oversize memory allocation. - ** - ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte - ** types use so much data space that there can only be 4096 and 32 of - ** them, respectively. So the maximum header length results from a - ** 3-byte type for each of the maximum of 32768 columns plus three - ** extra bytes for the header length itself. 32768*3 + 3 = 98307. - */ - if( offset > 98307 ){ - rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; - } - - /* Compute in len the number of bytes of data we need to read in order - ** to get nField type values. offset is an upper bound on this. But - ** nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*nField+3 might be smaller than offset. - ** We want to minimize len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused offset - ** to be oversized. Offset is limited to 98307 above. But 98307 might - ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, nField*5+3 - ** will likely be much smaller since nField will likely be less than - ** 20 or so. This insures that Robson memory allocation limits are - ** not exceeded even for corrupt database files. - */ - len = nField*5 + 3; - if( len > (int)offset ) len = (int)offset; - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite4VdbeMemFromBtree() to - ** acquire the complete header text. - */ - if( !zRec && availisIndex, &sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - zData = sMem.z; - } - zEndHdr = (u8 *)&zData[len]; - zIdx = (u8 *)&zData[szHdr]; - - /* Scan the header and use it to fill in the aType[] and aOffset[] - ** arrays. aType[i] will contain the type integer for the i-th - ** column and aOffset[i] will contain the offset from the beginning - ** of the record to the start of the data for the i-th column - */ - for(i=0; i zEndHdr) || (offset > payloadSize) - || (zIdx==zEndHdr && offset!=payloadSize) ){ - rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; - } - } - - /* Get the column information. If aOffset[p2] is non-zero, then - ** deserialize the value from the record. If aOffset[p2] is zero, - ** then there are not enough fields in the record to satisfy the - ** request. In this case, set the value NULL or to P4 if P4 is - ** a pointer to a Mem object. - */ - if( aOffset[p2] ){ - assert( rc==SQLITE_OK ); - if( zRec ){ - VdbeMemRelease(pDest); - sqlite4VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest); - }else{ - len = sqlite4VdbeSerialTypeLen(aType[p2]); - sqlite4VdbeMemMove(&sMem, pDest); - rc = sqlite4VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - zData = sMem.z; - sqlite4VdbeSerialGet((u8*)zData, aType[p2], pDest); - } - pDest->enc = encoding; - }else{ - if( pOp->p4type==P4_MEM ){ - sqlite4VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); - }else{ - MemSetTypeFlag(pDest, MEM_Null); - } - } - - /* If we dynamically allocated space to hold the data (in the - ** sqlite4VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the pDest structure. - ** This prevents a memory copy. - */ - if( sMem.zMalloc ){ - assert( sMem.z==sMem.zMalloc ); - assert( !(pDest->flags & MEM_Dyn) ); - assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); - pDest->flags &= ~(MEM_Ephem|MEM_Static); - pDest->flags |= MEM_Term; - pDest->z = sMem.z; - pDest->zMalloc = sMem.zMalloc; - } - - rc = sqlite4VdbeMemMakeWriteable(pDest); - -op_column_out: + aData = (const KVByteArray*)pReg->z; + nData = pReg->n; + }else{ + aData = 0; + MemSetTypeFlag(pDest, MEM_Null); + } + if( rc==SQLITE_OK && aData ){ + rc = sqlite4VdbeCreateDecoder(db, aData, nData, pC->nField, &pCodec); + if( rc==0 ){ + pDefault = (pOp->p4type==P4_MEM) ? pOp->p4.pMem : 0; + rc = sqlite4VdbeDecodeValue(pCodec, pOp->p2, pDefault, pDest); + sqlite4VdbeDestroyDecoder(pCodec); + } + }else{ + sqlite4VdbeMemSetNull(pDest); + } UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; } @@ -2430,30 +2157,26 @@ assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); assert( memIsValid(pIn1) ); - ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); pIn1++; } break; } /* Opcode: MakeKey P1 P2 * * * ** ** This must be followed immediately by a MakeRecord opcode. This -** opcode performs the subsequence MakeRecord but instead of generating -** a data record, generates a key for the cursor P1. The data is -** written to P2 of this opcode. A separate key structure is written to -** P3 of the subsequent MakeRecord opcode. +** opcode performs the subsequent MakeRecord and also generates +** a key for the cursor P1 and stores that key in register P2. */ /* Opcode: MakeRecord P1 P2 P3 P4 * ** -** Convert P2 registers beginning with P1 into the [record format] -** use as a data record in a database table and store the result in P4. -** The OP_Column opcode can decode the record later. +** Convert registers P1..P1+P2-1 into a data record and store the result +** in register P3. The OP_Column opcode can be used to decode the record. ** ** 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 ** field of the index key. ** @@ -2462,34 +2185,26 @@ ** ** If P4 is NULL then all index fields have the affinity NONE. */ case OP_MakeKey: case OP_MakeRecord: { - u8 *zNewRecord; /* A buffer to hold the data for the new record */ - Mem *pRec; /* The new record */ - u64 nData; /* Number of bytes of data space */ - int nHdr; /* Number of bytes of header space */ - i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ - int nVarint; /* Number of bytes in a varint */ - u32 serial_type; /* Type field */ Mem *pData0; /* First field to be combined into the record */ Mem *pLast; /* Last field of the record */ + Mem *pMem; /* For looping over inputs */ int nField; /* Number of fields in the record */ char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ - int i; /* Space used in zNewRecord[] */ - int len; /* Length of a field */ VdbeCursor *pC; /* Cursor to generate key for */ - u8 *aRec2; - int nRec2; + Mem *pKeyOut; /* Where to store the generated key */ + int keyReg; /* Register into which to write the key */ + u8 *aRec; /* The constructed key or value */ + int nRec; /* Size of aRec[] in bytes */ - nData = 0; /* Number of bytes of data space */ - nHdr = 0; /* Number of bytes of header space */ - nZero = 0; /* Number of zero bytes at the end of the record */ if( pOp->opcode==OP_MakeKey ){ pC = p->apCsr[pOp->p1]; + keyReg = pOp->p2; + pKeyOut = &aMem[keyReg]; + memAboutToChange(p, pKeyOut); assert( pC!=0 ); assert( pC->pKeyInfo!=0 ); pc++; pOp++; assert( pOp->opcode==OP_MakeRecord ); @@ -2500,287 +2215,85 @@ zAffinity = pOp->p4.z; assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; - file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3p1 || 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. + /* Loop through the input elements. Apply affinity to each one and + ** expand all zero-blobs. */ - for(pRec=pData0; pRec<=pLast; pRec++){ - assert( memIsValid(pRec) ); + for(pMem=pData0; pMem<=pLast; pMem++){ + assert( memIsValid(pMem) ); if( zAffinity ){ - applyAffinity(pRec, zAffinity[pRec-pData0], encoding); - } - if( pRec->flags&MEM_Zero && pRec->n>0 ){ - sqlite4VdbeMemExpandBlob(pRec); - } - serial_type = sqlite4VdbeSerialType(pRec, file_format); - len = sqlite4VdbeSerialTypeLen(serial_type); - nData += len; - nHdr += sqlite4VarintLen(serial_type); - if( pRec->flags & MEM_Zero ){ - /* Only pure zero-filled BLOBs can be input to this Opcode. - ** We do not allow blobs with a prefix and a zero-filled tail. */ - nZero += pRec->u.nZero; - }else if( len ){ - nZero = 0; + applyAffinity(pMem, *(zAffinity++), encoding); + } + if( pMem->flags&MEM_Zero ){ + sqlite4VdbeMemExpandBlob(pMem); } } - aRec2 = 0; + /* Compute the key (if this is a MakeKey opcode) */ if( pC ){ - sqlite4VdbeEncodeKey(db, pData0, nField, pC->iRoot, pC->pKeyInfo, - &aRec2, &nRec2); - }else{ - sqlite4VdbeEncodeData(db, pData0, nField, &aRec2, &nRec2); - } - if( aRec2 ){ -#if 0 - printf(pC ? "KEY:":"DATA:"); - for(i=0; idb->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - /* Make sure the output register has a buffer large enough to store - ** the new record. The output register (pOp->p3) is not allowed to - ** be one of the input registers (because the following call to - ** sqlite4VdbeMemGrow() could clobber the value before it is used). - */ - if( sqlite4VdbeMemGrow(pOut, (int)nByte, 0) ){ - goto no_mem; - } - zNewRecord = (u8 *)pOut->z; - - /* Write the record */ - i = putVarint32(zNewRecord, nHdr); - for(pRec=pData0; pRec<=pLast; pRec++){ - serial_type = sqlite4VdbeSerialType(pRec, file_format); - i += putVarint32(&zNewRecord[i], serial_type); /* serial type */ - } - for(pRec=pData0; pRec<=pLast; pRec++){ /* serial data */ - i += sqlite4VdbeSerialPut(&zNewRecord[i], (int)(nByte-i), pRec,file_format); - } - assert( i==nByte ); - - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut->n = (int)nByte; - pOut->flags = MEM_Blob | MEM_Dyn; - pOut->xDel = 0; - if( nZero ){ - pOut->u.nZero = nZero; - pOut->flags |= MEM_Zero; - } - pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ - REGISTER_TRACE(pOp->p3, pOut); - UPDATE_MAX_BLOBSIZE(pOut); + aRec = 0; + rc = sqlite4VdbeEncodeKey(db, pData0, nField, pC->iRoot, pC->pKeyInfo, + &aRec, &nRec, 0); + if( rc ){ + sqlite4DbFree(db, aRec); + }else{ + rc = sqlite4VdbeMemSetStr(pKeyOut, aRec, nRec, 0, SQLITE_DYNAMIC); + REGISTER_TRACE(keyReg, pKeyOut); + UPDATE_MAX_BLOBSIZE(pKeyOut); + } + } + + /* Compute the value */ + if( rc==SQLITE_OK ){ + aRec = 0; + rc = sqlite4VdbeEncodeData(db, pData0, nField, &aRec, &nRec); + if( rc ){ + sqlite4DbFree(db, aRec); + }else{ + rc = sqlite4VdbeMemSetStr(pOut, aRec, nRec, 0, SQLITE_DYNAMIC); + REGISTER_TRACE(pOp->p3, pOut); + UPDATE_MAX_BLOBSIZE(pOut); + } + } break; } /* Opcode: Count P1 P2 * * * ** ** Store the number of entries (an integer value) in the table or index ** opened by cursor P1 in register P2 */ -#ifndef SQLITE_OMIT_BTREECOUNT case OP_Count: { /* out2-prerelease */ i64 nEntry; - BtCursor *pCrsr; - - pCrsr = p->apCsr[pOp->p1]->pCursor; - if( ALWAYS(pCrsr) ){ - rc = sqlite4BtreeCount(pCrsr, &nEntry); - }else{ - nEntry = 0; - } - pOut->u.i = nEntry; + VdbeCursor *pC; + + pC = p->apCsr[pOp->p1]; + rc = sqlite4VdbeSeekEnd(pC, +1); + nEntry = 0; + while( rc!=SQLITE_NOTFOUND ){ + nEntry++; + rc = sqlite4VdbeNext(pC); + } + sqlite4VdbeMemSetInt64(pOut, nEntry); break; } -#endif /* Opcode: Savepoint P1 * * P4 * ** ** Open, release or rollback the savepoint named by parameter P4, depending ** on the value of P1. To open a new savepoint, P1==0. To release (commit) an ** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. */ case OP_Savepoint: { - int p1; /* Value of P1 operand */ - char *zName; /* Name of savepoint */ - int nName; - Savepoint *pNew; - Savepoint *pSavepoint; - Savepoint *pTmp; - int iSavepoint; - int ii; - - p1 = pOp->p1; - zName = pOp->p4.z; - - /* Assert that the p1 parameter is valid. Also that if there is no open - ** transaction, then there cannot be any savepoints. - */ - assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK ); - assert( db->pSavepoint || db->isTransactionSavepoint==0 ); - assert( checkSavepointCount(db) ); - - if( p1==SAVEPOINT_BEGIN ){ - if( db->writeVdbeCnt>0 ){ - /* A new savepoint cannot be created if there are active write - ** statements (i.e. open read/write incremental blob handles). - */ - sqlite4SetString(&p->zErrMsg, db, "cannot open savepoint - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else{ - nName = sqlite4Strlen30(zName); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* This call is Ok even if this savepoint is actually a transaction - ** savepoint (and therefore should not prompt xSavepoint()) callbacks. - ** If this is a transaction savepoint being opened, it is guaranteed - ** that the db->aVTrans[] array is empty. */ - assert( db->autoCommit==0 || db->nVTrans==0 ); - rc = sqlite4VtabSavepoint(db, SAVEPOINT_BEGIN, - db->nStatement+db->nSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; -#endif - - /* Create a new savepoint structure. */ - pNew = sqlite4DbMallocRaw(db, sizeof(Savepoint)+nName+1); - if( pNew ){ - pNew->zName = (char *)&pNew[1]; - memcpy(pNew->zName, zName, nName+1); - - /* If there is no open transaction, then mark this as a special - ** "transaction savepoint". */ - if( db->autoCommit ){ - db->autoCommit = 0; - db->isTransactionSavepoint = 1; - }else{ - db->nSavepoint++; - } - - /* Link the new savepoint into the database handle's list. */ - pNew->pNext = db->pSavepoint; - db->pSavepoint = pNew; - pNew->nDeferredCons = db->nDeferredCons; - } - } - }else{ - iSavepoint = 0; - - /* Find the named savepoint. If there is no such savepoint, then an - ** an error is returned to the user. */ - for( - pSavepoint = db->pSavepoint; - pSavepoint && sqlite4StrICmp(pSavepoint->zName, zName); - pSavepoint = pSavepoint->pNext - ){ - iSavepoint++; - } - if( !pSavepoint ){ - sqlite4SetString(&p->zErrMsg, db, "no such savepoint: %s", zName); - rc = SQLITE_ERROR; - }else if( - db->writeVdbeCnt>0 || (p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) - ){ - /* It is not possible to release (commit) a savepoint if there are - ** active write statements. It is not possible to rollback a savepoint - ** if there are any active statements at all. - */ - sqlite4SetString(&p->zErrMsg, db, - "cannot %s savepoint - SQL statements in progress", - (p1==SAVEPOINT_ROLLBACK ? "rollback": "release") - ); - rc = SQLITE_BUSY; - }else{ - - /* Determine whether or not this is a transaction savepoint. If so, - ** and this is a RELEASE command, then the current transaction - ** is committed. - */ - int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && p1==SAVEPOINT_RELEASE ){ - if( (rc = sqlite4VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - } - db->autoCommit = 1; - if( sqlite4VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; - db->autoCommit = 0; - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - db->isTransactionSavepoint = 0; - rc = p->rc; - }else{ - iSavepoint = db->nSavepoint - iSavepoint - 1; - for(ii=0; iinDb; ii++){ - rc = sqlite4BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - } - if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ - sqlite4ExpirePreparedStatements(db); - sqlite4ResetInternalSchema(db, -1); - db->flags = (db->flags | SQLITE_InternChanges); - } - } - - /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all - ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=pSavepoint ){ - pTmp = db->pSavepoint; - db->pSavepoint = pTmp->pNext; - sqlite4DbFree(db, pTmp); - db->nSavepoint--; - } - - /* If it is a RELEASE, then destroy the savepoint being operated on - ** too. If it is a ROLLBACK TO, then set the number of deferred - ** constraint violations present in the database to the value stored - ** when the savepoint was created. */ - if( p1==SAVEPOINT_RELEASE ){ - assert( pSavepoint==db->pSavepoint ); - db->pSavepoint = pSavepoint->pNext; - sqlite4DbFree(db, pSavepoint); - if( !isTransaction ){ - db->nSavepoint--; - } - }else{ - db->nDeferredCons = pSavepoint->nDeferredCons; - } - - if( !isTransaction ){ - rc = sqlite4VtabSavepoint(db, p1, iSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - } - break; } /* Opcode: AutoCommit P1 P2 * * * ** @@ -2790,170 +2303,60 @@ ** there are active writing VMs or active VMs that use shared cache. ** ** This instruction causes the VM to halt. */ case OP_AutoCommit: { - int desiredAutoCommit; - int iRollback; - int turnOnAC; - - desiredAutoCommit = pOp->p1; - iRollback = pOp->p2; - turnOnAC = desiredAutoCommit && !db->autoCommit; - assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); - assert( desiredAutoCommit==1 || iRollback==0 ); - assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ - - if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){ - /* If this instruction implements a ROLLBACK and other VMs are - ** still running, and a transaction is active, return an error indicating - ** that the other VMs must complete first. - */ - sqlite4SetString(&p->zErrMsg, db, "cannot rollback transaction - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else if( turnOnAC && !iRollback && db->writeVdbeCnt>0 ){ - /* If this instruction implements a COMMIT and other VMs are writing - ** return an error indicating that the other VMs must complete first. - */ - sqlite4SetString(&p->zErrMsg, db, "cannot commit transaction - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else if( desiredAutoCommit!=db->autoCommit ){ - if( iRollback ){ - assert( desiredAutoCommit==1 ); - sqlite4RollbackAll(db); - db->autoCommit = 1; - }else if( (rc = sqlite4VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - }else{ - db->autoCommit = (u8)desiredAutoCommit; - if( sqlite4VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; - db->autoCommit = (u8)(1-desiredAutoCommit); - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - } - assert( db->nStatement==0 ); - sqlite4CloseSavepoints(db); - if( p->rc==SQLITE_OK ){ - rc = SQLITE_DONE; - }else{ - rc = SQLITE_ERROR; - } - goto vdbe_return; - }else{ - sqlite4SetString(&p->zErrMsg, db, - (!desiredAutoCommit)?"cannot start a transaction within a transaction":( - (iRollback)?"cannot rollback - no transaction is active": - "cannot commit - no transaction is active")); - - rc = SQLITE_ERROR; - } break; } /* Opcode: Transaction P1 P2 * * * ** -** Begin a transaction. The transaction ends when a Commit or Rollback -** opcode is encountered. Depending on the ON CONFLICT setting, the -** transaction might also be rolled back if an error is encountered. +** Begin a transaction. ** ** P1 is the index of the database file on which the transaction is ** started. Index 0 is the main database file and index 1 is the ** file used for temporary tables. Indices of 2 or more are used for ** attached databases. ** -** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is -** obtained on the database file when a write-transaction is started. No -** other process can start another write transaction while this transaction is -** underway. Starting a write transaction also creates a rollback journal. A -** write transaction must be started before any changes can be made to the -** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained -** on the file. +** If P2 is non-zero, then a write-transaction is started. If P2 is zero +** then a read-transaction is started. ** -** If a write-transaction is started and the Vdbe.usesStmtJournal flag is +** If a write-transaction is started and the Vdbe.needSavepoint flag is ** true (this flag is set if the Vdbe may modify more than one row and may ** throw an ABORT exception), a statement transaction may also be opened. ** More specifically, a statement transaction is opened iff the database ** connection is currently not in autocommit mode, or if there are other ** active statements. A statement transaction allows the affects of this ** VDBE to be rolled back after an error without having to roll back the ** entire transaction. If no error is encountered, the statement transaction ** will automatically commit when the VDBE halts. -** -** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { - Btree *pBt; + Db *pDb; + KVStore *pKV; + int needStmt; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - pBt = db->aDb[pOp->p1].pBt; - - if( pBt ){ - rc = sqlite4BtreeBeginTrans(pBt, pOp->p2); - if( rc==SQLITE_BUSY ){ - p->pc = pc; - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - - if( pOp->p2 && p->usesStmtJournal - && (db->autoCommit==0 || db->activeVdbeCnt>1) - ){ - assert( sqlite4BtreeIsInTrans(pBt) ); - if( p->iStatement==0 ){ - assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; - p->iStatement = db->nSavepoint + db->nStatement; - } - - rc = sqlite4VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); + pDb = &db->aDb[pOp->p1]; + pKV = pDb->pKV; + if( pOp->p2==0 ){ + /* Read transaction needed. Start if we are not already in one. */ + if( pKV->iTransLevel==0 ){ + rc = sqlite4KVStoreBegin(pKV, 1); + } + }else{ + /* A write transaction is needed */ + needStmt = pKV->iTransLevel>0 && (p->needSavepoint || db->activeVdbeCnt>1); + if( pKV->iTransLevel<2 ){ + rc = sqlite4KVStoreBegin(pKV, 2); + }else if( p->needSavepoint ){ + rc = sqlite4KVStoreBegin(pKV, pKV->iTransLevel+1); if( rc==SQLITE_OK ){ - rc = sqlite4BtreeBeginStmt(pBt, p->iStatement); - } - - /* Store the current value of the database handles deferred constraint - ** counter. If the statement transaction needs to be rolled back, - ** the value of this counter needs to be restored too. */ - p->nStmtDefCons = db->nDeferredCons; - } - } - break; -} - -/* Opcode: ReadCookie P1 P2 P3 * * -** -** Read cookie number P3 from database P1 and write it into register P2. -** P3==1 is the schema version. P3==2 is the database format. -** P3==3 is the recommended pager cache size, and so forth. P1==0 is -** the main database file and P1==1 is the database file used to store -** temporary tables. -** -** There must be a read-lock on the database (either a transaction -** must be started or there must be an open cursor) before -** executing this instruction. -*/ -case OP_ReadCookie: { /* out2-prerelease */ - int iMeta; - int iDb; - int iCookie; - - iDb = pOp->p1; - iCookie = pOp->p3; - assert( pOp->p3=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[iDb].pBt, iCookie, (u32 *)&iMeta); - pOut->u.i = iMeta; + p->stmtTransMask |= ((yDbMask)1)<p1; + } + } + } break; } /* Opcode: SetCookie P1 P2 P3 * * ** @@ -2965,28 +2368,21 @@ ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ Db *pDb; - assert( pOp->p2p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - assert( sqlite4SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite4VdbeMemIntegerify(pIn3); - /* See note about index shifting on OP_ReadCookie */ - rc = sqlite4BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i); - if( pOp->p2==BTREE_SCHEMA_VERSION ){ - /* When the schema cookie changes, record the new cookie internally */ - pDb->pSchema->schema_cookie = (int)pIn3->u.i; - db->flags |= SQLITE_InternChanges; - }else if( pOp->p2==BTREE_FILE_FORMAT ){ - /* Record changes in the file format */ - pDb->pSchema->file_format = (u8)pIn3->u.i; - } + v = (u32)pIn3->u.i; + rc = sqlite4KVStorePutMeta(db, pDb->pKV, 0, 1, &v); + pDb->pSchema->schema_cookie = (int)pIn3->u.i; + db->flags |= SQLITE_InternChanges; if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite4ExpirePreparedStatements(db); p->expired = 0; @@ -3013,18 +2409,19 @@ ** invoked. */ case OP_VerifyCookie: { int iMeta; int iGen; - Btree *pBt; + KVStore *pKV; + KVByteArray *aData; + KVSize nData; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - assert( sqlite4SchemaMutexHeld(db, pOp->p1, 0) ); - pBt = db->aDb[pOp->p1].pBt; - if( pBt ){ - sqlite4BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + pKV = db->aDb[pOp->p1].pKV; + if( pKV ){ + rc = sqlite4KVStoreGetMeta(pKV, 0, 1, &iMeta); + if( rc ) break; iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ iGen = iMeta = 0; } if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){ @@ -3106,12 +2503,11 @@ case OP_OpenWrite: { int nField; KeyInfo *pKeyInfo; int p2; int iDb; - int wrFlag; - Btree *pX; + KVStore *pX; VdbeCursor *pCur; Db *pDb; if( p->expired ){ rc = SQLITE_ABORT; @@ -3121,23 +2517,13 @@ nField = 0; pKeyInfo = 0; p2 = pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDbnDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[iDb]; - pX = pDb->pBt; + pX = pDb->pKV; assert( pX!=0 ); - if( pOp->opcode==OP_OpenWrite ){ - wrFlag = 1; - assert( sqlite4SchemaMutexHeld(db, iDb, 0) ); - if( pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = pDb->pSchema->file_format; - } - }else{ - wrFlag = 0; - } if( pOp->p5 ){ assert( p2>0 ); assert( p2<=p->nMem ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); @@ -3164,17 +2550,13 @@ pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; pCur->isOrdered = 1; pCur->iRoot = p2; - rc = sqlite4BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); + rc = sqlite4KVStoreOpenCursor(pX, &pCur->pKVCur); pCur->pKeyInfo = pKeyInfo; - /* Since it performs no memory allocation or IO, the only value that - ** sqlite4BtreeCursor() may return is SQLITE_OK. */ - assert( rc==SQLITE_OK ); - /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; @@ -3213,52 +2595,19 @@ ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; - static const int vfsFlags = - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | - SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - rc = sqlite4BtreeOpen(db->pVfs, 0, db, &pCx->pBt, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); - if( rc==SQLITE_OK ){ - sqlite4KVStoreOpen(":memory:", &pCx->pTmpKV); - if( pCx->pTmpKV ) sqlite4KVStoreBegin(pCx->pTmpKV, 2); - rc = sqlite4BtreeBeginTrans(pCx->pBt, 1); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite4BtreeCreateTable() 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 BLOB_INTKEY table). - */ - if( pOp->p4.pKeyInfo ){ - int pgno; - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite4BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - rc = sqlite4BtreeCursor(pCx->pBt, pgno, 1, - (KeyInfo*)pOp->p4.z, pCx->pCursor); - pCx->pKeyInfo = pOp->p4.pKeyInfo; - pCx->pKeyInfo->enc = ENC(p->db); - } - pCx->isTable = 0; - }else{ - rc = sqlite4BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor); - pCx->isTable = 1; - } - } - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); + rc = sqlite4KVStoreOpen(db, "ephm", ":memory:", &pCx->pTmpKV, + SQLITE_KVOPEN_TEMPORARY | SQLITE_KVOPEN_NO_TRANSACTIONS); + pCx->pKeyInfo = pOp->p4.pKeyInfo; + if( pCx->pKeyInfo ) pCx->pKeyInfo->enc = ENC(p->db); pCx->isIndex = !pCx->isTable; break; } /* Opcode: OpenSorter P1 P2 * P4 * @@ -3266,11 +2615,11 @@ ** This opcode works like OP_OpenEphemeral except that it opens ** a transient index that is specifically designed to sort large ** tables using an external merge-sort algorithm. */ case OP_SorterOpen: { - VdbeCursor *pCx; + /* VdbeCursor *pCx; */ #ifndef SQLITE_OMIT_MERGE_SORT pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; pCx->pKeyInfo->enc = ENC(p->db); @@ -3377,16 +2726,20 @@ */ case OP_SeekLt: /* jump, in3 */ case OP_SeekLe: /* jump, in3 */ case OP_SeekGe: /* jump, in3 */ case OP_SeekGt: { /* jump, in3 */ - int res; int oc; VdbeCursor *pC; - UnpackedRecord r; int nField; - i64 iKey; /* The rowid we are to seek to */ + KVByteArray *aProbe; + KVSize nProbe; + const KVByteArray *aKey; + KVSize nKey; + int c; + int n; + sqlite4_uint64 iRoot; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -3393,341 +2746,103 @@ assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); assert( pC->isOrdered ); - if( ALWAYS(pC->pCursor!=0) ){ - oc = pOp->opcode; - pC->nullRow = 0; - if( pC->isTable ){ - /* The input value in P3 might be of any type: integer, real, string, - ** blob, or NULL. But it needs to be an integer before we can do - ** the seek, so covert it. */ - pIn3 = &aMem[pOp->p3]; - applyNumericAffinity(pIn3); - iKey = sqlite4VdbeIntValue(pIn3); - pC->rowidIsValid = 0; - - /* If the P3 value could not be converted into an integer without - ** loss of information, then special processing is required... */ - if( (pIn3->flags & MEM_Int)==0 ){ - if( (pIn3->flags & MEM_Real)==0 ){ - /* If the P3 value cannot be converted into any kind of a number, - ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; - break; - } - /* If we reach this point, then the P3 value must be a floating - ** point number. */ - assert( (pIn3->flags & MEM_Real)!=0 ); - - if( iKey==SMALLEST_INT64 && (pIn3->r<(double)iKey || pIn3->r>0) ){ - /* The P3 value is too large in magnitude to be expressed as an - ** integer. */ - res = 1; - if( pIn3->r<0 ){ - if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); - rc = sqlite4BtreeFirst(pC->pCursor, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - }else{ - if( oc<=OP_SeekLe ){ assert( oc==OP_SeekLt || oc==OP_SeekLe ); - rc = sqlite4BtreeLast(pC->pCursor, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - if( res ){ - pc = pOp->p2 - 1; - } - break; - }else if( oc==OP_SeekLt || oc==OP_SeekGe ){ - /* Use the ceiling() function to convert real->int */ - if( pIn3->r > (double)iKey ) iKey++; - }else{ - /* Use the floor() function to convert real->int */ - assert( oc==OP_SeekLe || oc==OP_SeekGt ); - if( pIn3->r < (double)iKey ) iKey--; - } - } - rc = sqlite4BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( res==0 ){ - pC->rowidIsValid = 1; - pC->lastRowid = iKey; - } - }else{ - nField = pOp->p4.i; - assert( pOp->p4type==P4_INT32 ); - assert( nField>0 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)nField; - - /* The next line of code computes as follows, only faster: - ** if( oc==OP_SeekGt || oc==OP_SeekLe ){ - ** r.flags = UNPACKED_INCRKEY; - ** }else{ - ** r.flags = 0; - ** } - */ - r.flags = (u16)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); - assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekGe || r.flags==0 ); - assert( oc!=OP_SeekLt || r.flags==0 ); - - r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipCursor, &r, 0, 0, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pC->rowidIsValid = 0; - } - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; -#ifdef SQLITE_TEST - sqlite4_search_count++; -#endif - if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); - if( res<0 || (res==0 && oc==OP_SeekGt) ){ - rc = sqlite4BtreeNext(pC->pCursor, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - pC->rowidIsValid = 0; - }else{ - res = 0; - } - }else{ - assert( oc==OP_SeekLt || oc==OP_SeekLe ); - if( res>0 || (res==0 && oc==OP_SeekLt) ){ - rc = sqlite4BtreePrevious(pC->pCursor, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - pC->rowidIsValid = 0; - }else{ - /* res might be negative because the table is empty. Check to - ** see if this is the case. - */ - res = sqlite4BtreeEof(pC->pCursor); - } - } - assert( pOp->p2>0 ); - if( res ){ - pc = pOp->p2 - 1; - } - }else{ - /* This happens when attempting to open the sqlite4_master table - ** for read access returns SQLITE_EMPTY. In this case always - ** take the jump (since there are no records in the table). - */ + oc = pOp->opcode; + pC->nullRow = 0; + if( pC->isTable ){ + nField = 1; + }else{ + nField = pOp->p4.i; + } + rc = sqlite4VdbeEncodeKey(db, pIn3, nField, pC->iRoot, pC->pKeyInfo, + &aProbe, &nProbe, 0); + if( rc ){ + sqlite4DbFree(db, aProbe); + break; + } + rc = sqlite4KVCursorSeek(pC->pKVCur, aProbe, nProbe, + oc<=OP_SeekLe ? -1 : 1); + sqlite4DbFree(db, aProbe); + if( rc==SQLITE_OK ){ + if( oc==OP_SeekLt ){ + rc = sqlite4KVCursorPrev(pC->pKVCur); + }else if( oc==OP_SeekGt ){ + rc = sqlite4KVCursorNext(pC->pKVCur); + } + }else if( rc==SQLITE_INEXACT ){ + rc = SQLITE_OK; + } + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + iRoot = 0; + n = sqlite4GetVarint64(aKey, nKey, &iRoot); + if( iRoot!=pC->iRoot ) rc = SQLITE_DONE; + c = aKey[n]; + if( c<0x05 || c>0xfa ) rc = SQLITE_DONE; + } + } + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; pc = pOp->p2 - 1; } break; } /* Opcode: Seek P1 P2 * * * ** ** P1 is an open table cursor and P2 is a rowid integer. Arrange ** for P1 to move so that it points to the rowid given by P2. -** -** This is actually a deferred seek. Nothing actually happens until -** the cursor is used to read a record. That way, if no reads -** occur, no unnecessary I/O happens. */ case OP_Seek: { /* in2 */ VdbeCursor *pC; + KVCursor *pKVCur; + KVByteArray *aKey; + KVSize nKey; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - if( ALWAYS(pC->pCursor!=0) ){ - assert( pC->isTable ); - pC->nullRow = 0; - pIn2 = &aMem[pOp->p2]; - pC->movetoTarget = sqlite4VdbeIntValue(pIn2); - pC->rowidIsValid = 0; - pC->deferredMoveto = 1; - } + assert( pC->isTable ); + pKVCur = pC->pKVCur; + rc = sqlite4VdbeEncodeKey(db, aMem+pOp->p2, 1, pC->iRoot, 0, + &aKey, &nKey, 0); + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorSeek(pKVCur, aKey, nKey, 0); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_CORRUPT_BKPT; + } + sqlite4DbFree(db, aKey); break; } /* Opcode: Found P1 P2 P3 P4 * ** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. +** If P4==0 then register P3 holds a blob constructed by MakeKey. If +** P4>0 then register P3 is the first of P4 registers that should be +** combined to generate a key. ** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** Cursor P1 is open on an index. If the record identified by P3 and P4 ** is a prefix of any entry in P1 then a jump is made to P2 and ** P1 is left pointing at the matching entry. */ /* Opcode: NotFound P1 P2 P3 P4 * ** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. +** If P4==0 then register P3 holds a blob constructed by MakeKey. If +** P4>0 then register P3 is the first of P4 registers that should be +** combined to generate key. ** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** Cursor P1 is on an index. If the record identified by P3 and P4 ** is not the prefix of any entry in P1 then a jump is made to P2. If P1 ** does contain an entry whose prefix matches the P3/P4 record then control ** falls through to the next instruction and P1 is left pointing at the ** matching entry. ** ** See also: Found, NotExists, IsUnique */ -case OP_NotFound: /* jump, in3 */ -case OP_Found: { /* jump, in3 */ - int alreadyExists; - VdbeCursor *pC; - int res; - char *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; - char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; - -#ifdef SQLITE_TEST - sqlite4_found_count++; -#endif - - alreadyExists = 0; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p4type==P4_INT32 ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pIn3 = &aMem[pOp->p3]; - if( ALWAYS(pC->pCursor!=0) ){ - - assert( pC->isTable==0 ); - if( pOp->p4.i>0 ){ - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; - r.aMem = pIn3; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipKeyInfo, aTempRec, sizeof(aTempRec), &pFree - ); - if( pIdxKey==0 ) goto no_mem; - assert( pIn3->flags & MEM_Blob ); - assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ - sqlite4VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); - pIdxKey->flags |= UNPACKED_PREFIX_MATCH; - } - rc = sqlite4BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); - if( pOp->p4.i==0 ){ - sqlite4DbFree(db, pFree); - } - if( rc!=SQLITE_OK ){ - break; - } - alreadyExists = (res==0); - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - } - if( pOp->opcode==OP_Found ){ - if( alreadyExists ) pc = pOp->p2 - 1; - }else{ - if( !alreadyExists ) pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: IsUnique P1 P2 P3 P4 * -** -** Cursor P1 is open on an index b-tree - that is to say, a btree which -** no data and where the keys are records generated by OP_MakeRecord with -** the last field being the integer ROWID of the entry that the index -** entry refers to. -** -** The P3 register contains an integer record number. Call this record -** number R. Register P4 is the first in a set of N contiguous registers -** that make up an unpacked index key that can be used with cursor P1. -** The value of N can be inferred from the cursor. N includes the rowid -** value appended to the end of the index record. This rowid value may -** or may not be the same as R. -** -** If any of the N registers beginning with register P4 contains a NULL -** value, jump immediately to P2. -** -** Otherwise, this instruction checks if cursor P1 contains an entry -** where the first (N-1) fields match but the rowid value at the end -** of the index entry is not R. If there is no such entry, control jumps -** to instruction P2. Otherwise, the rowid of the conflicting index -** entry is copied to register P3 and control falls through to the next -** instruction. -** -** See also: NotFound, NotExists, Found -*/ -case OP_IsUnique: { /* jump, in3 */ - u16 ii; - VdbeCursor *pCx; - BtCursor *pCrsr; - u16 nField; - Mem *aMx; - UnpackedRecord r; /* B-Tree index search key */ - i64 R; /* Rowid stored in register P3 */ - - pIn3 = &aMem[pOp->p3]; - aMx = &aMem[pOp->p4.i]; - /* Assert that the values of parameters P1 and P4 are in range. */ - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - - /* Find the index cursor. */ - pCx = p->apCsr[pOp->p1]; - assert( pCx->deferredMoveto==0 ); - pCx->seekResult = 0; - pCx->cacheStatus = CACHE_STALE; - pCrsr = pCx->pCursor; - - /* If any of the values are NULL, take the jump. */ - nField = pCx->pKeyInfo->nField; - for(ii=0; iip2 - 1; - pCrsr = 0; - break; - } - } - assert( (aMx[nField].flags & MEM_Null)==0 ); - - if( pCrsr!=0 ){ - /* Populate the index search key. */ - r.pKeyInfo = pCx->pKeyInfo; - r.nField = nField + 1; - r.flags = UNPACKED_PREFIX_SEARCH; - r.aMem = aMx; -#ifdef SQLITE_DEBUG - { int i; for(i=0; iu.i; - - /* Search the B-Tree index. If no conflicting record is found, jump - ** to P2. Otherwise, copy the rowid of the conflicting record to - ** register P3 and fall through to the next instruction. */ - rc = sqlite4BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &pCx->seekResult); - if( (r.flags & UNPACKED_PREFIX_SEARCH) || r.rowid==R ){ - pc = pOp->p2 - 1; - }else{ - pIn3->u.i = r.rowid; - } - } - break; -} - /* Opcode: NotExists P1 P2 P3 * * ** ** Use the content of register P3 as an 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 through. The cursor is left @@ -3738,46 +2853,169 @@ ** NotFound assumes key is a blob constructed from MakeRecord and ** P1 is an index. ** ** See also: Found, NotFound, IsUnique */ -case OP_NotExists: { /* jump, in3 */ +case OP_NotExists: { /* jump, in3 */ + pOp->p4.i = 1; + pOp->p4type = P4_INT32; + /* Fall through into OP_NotFound */ +} +case OP_NotFound: /* jump, in3 */ +case OP_Found: { /* jump, in3 */ + int alreadyExists; VdbeCursor *pC; - BtCursor *pCrsr; - int res; - u64 iKey; + KVByteArray *pFree; + KVByteArray *pProbe; + KVSize nProbe; + const KVByteArray *pKey; + KVSize nKey; + +#ifdef SQLITE_TEST + sqlite4_found_count++; +#endif + + alreadyExists = 0; + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p4type==P4_INT32 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pIn3 = &aMem[pOp->p3]; + assert( pC->pKVCur!=0 ); + assert( pC->isTable==0 || pOp->opcode==OP_NotExists ); + if( pOp->p4.i>0 ){ + rc = sqlite4VdbeEncodeKey(db, pIn3, pOp->p4.i, pC->iRoot, + pC->pKeyInfo, &pProbe, &nProbe, 0); + pFree = pProbe; + }else{ + pProbe = (KVByteArray*)pIn3->z; + nProbe = pIn3->n; + pFree = 0; + } + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe, nProbe, +1); + if( rc==SQLITE_INEXACT || rc==SQLITE_OK ){ + rc = sqlite4KVCursorKey(pC->pKVCur, &pKey, &nKey); + if( rc==SQLITE_OK && nKey>=nProbe && memcmp(pKey, pProbe, nKey)==0 ){ + alreadyExists = 1; + pC->nullRow = 0; + } + } + } + sqlite4DbFree(db, pFree); + if( pOp->opcode==OP_Found ){ + if( alreadyExists ) pc = pOp->p2 - 1; + }else{ + if( !alreadyExists ) pc = pOp->p2 - 1; + } + break; +} + +/* Opcode: IsUnique P1 P2 P3 P4 * +** +** Cursor P1 is open on an index. +** +** The P3 register contains an integer record number. Call this record +** number R. Register P4 is the first in a set of N contiguous registers +** that make up an unpacked index key that can be used with cursor P1. +** The value of N can be inferred from the KeyInfo.nField of the cursor. +** N includes the rowid value appended to the end of the index record. +** This rowid value may or may not be the same as R. +** +** If any of the N registers beginning with register P4 contains a NULL +** value, jump immediately to P2. +** +** Otherwise, this instruction checks if cursor P1 contains an entry +** where the first (N-1) fields match but the rowid value at the end +** of the index entry is not R. If there is no such entry (meaning that +** a row about to be inserted with rowid R is unique) then control jumps +** to instruction P2. Otherwise, the rowid of the conflicting index +** entry is copied to register P3 and control falls through to the next +** instruction. +** +** See also: NotFound, NotExists, Found +*/ +case OP_IsUnique: { /* jump, in3 */ +#if 0 + u16 ii; + VdbeCursor *pCx; + KVCursor *pKVCur; + u16 nField; + Mem *aMx; + KVByteArray *pProbe; + KVSize nProbe; + KVSize nShort; + KVSize nData; + int isUnique; + i64 R; /* Rowid stored in register P3 */ pIn3 = &aMem[pOp->p3]; - assert( pIn3->flags & MEM_Int ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->isTable ); - assert( pC->pseudoTableReg==0 ); - pCrsr = pC->pCursor; - if( ALWAYS(pCrsr!=0) ){ - res = 0; - iKey = pIn3->u.i; - rc = sqlite4BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); - pC->lastRowid = pIn3->u.i; - pC->rowidIsValid = res==0 ?1:0; - pC->nullRow = 0; - pC->cacheStatus = CACHE_STALE; - pC->deferredMoveto = 0; - if( res!=0 ){ - pc = pOp->p2 - 1; - assert( pC->rowidIsValid==0 ); - } - pC->seekResult = res; - }else{ - /* This happens when an attempt to open a read cursor on the - ** sqlite_master table returns SQLITE_EMPTY. - */ - pc = pOp->p2 - 1; - assert( pC->rowidIsValid==0 ); - pC->seekResult = 0; - } + aMx = &aMem[pOp->p4.i]; + /* Assert that the values of parameters P1 and P4 are in range. */ + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); + assert( pOp->p1>=0 && pOp->p1nCursor ); + + /* Find the index cursor. */ + pCx = p->apCsr[pOp->p1]; + pCx->seekResult = 0; + pCx->cacheStatus = CACHE_STALE; + pKVCur = pCx->pKVCur; + nField = pCx->pKeyInfo->nField; + + /* If any of the values are NULL, take the jump. */ + nField = pCx->pKeyInfo->nField; + for(ii=0; iip2 - 1; + pCrsr = 0; + break; + } + } + assert( (aMx[nField].flags & MEM_Null)==0 ); + + isUnique = 1; + if( pCrsr!=0 ){ + /* Extract the value of R from register P3. */ + sqlite4VdbeMemIntegerify(pIn3); + R = pIn3->u.i; + + /* Generate the probe key */ + rc = sqlite4VdbeEncodeKey(db, pIn3, nField, pCx->iRoot, + pCx->pKeyInfo, &pProbe, &nProbe, &nShort); + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorSeek(pKVCur, pProbe, nProbe, +1); + if( rc==SQLITE_OK ){ + /* Full key already exists in the index. Not unique. */ + isUnique = 0; + }else if( rc==SQLITE_INEXACT ){ + int c = sqlite4KVCursorCompare(pKVCur, pProbe, nShort); + if( c>0 ){ + rc = sqlite4KVCursorPrev(pKVCur); + if( rc==SQLITE_OK ){ + c = sqlite4KVCursorCompare(pKVCur, pProbe, nShort); + } + } + if( c ) isUnique = 0; + } + sqlite4DbFree(db, pProbe); + if( isUnique ){ + pc = pOp->p2 - 1; + }else{ + /* Collision. Copy the conflicting rowid into register P3. */ + rc = sqlite4KVCursorData(pKVCur, 0, -1, &aData, &nData); + if( rc==SQLITE_OK ){ + rc = sqlite4VdbeCreateDecoder(db, aData, nData, nField, &pCodec); + if( rc==SQLITE_OK ){ + rc = sqlite4VdbeDecodeValue(pCodec, nField-1, 0, pIn3); + sqlite4VdbeDestroyDecoder(pCodec); + } + } + } + } + } +#endif break; } /* Opcode: Sequence P1 P2 * * * ** @@ -3798,150 +3036,64 @@ ** ** Get a new integer record number (a.k.a "rowid") used as the key to a table. ** The record number is not previously used as a key in the database ** table that cursor P1 points to. The new record number is written ** to register P2. -** -** If P3>0 then P3 is a register in the root frame of this VDBE that holds -** the largest previously generated record number. No new record numbers are -** allowed to be less than this value. When this value reaches its maximum, -** an SQLITE_FULL error is generated. The P3 register is updated with the ' -** generated record number. This P3 mechanism is used to help implement the -** AUTOINCREMENT feature. */ case OP_NewRowid: { /* out2-prerelease */ - i64 v; /* The new rowid */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlite4BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ + i64 v; /* The new rowid */ + VdbeCursor *pC; /* Cursor of table to get the new rowid */ + const KVByteArray *aKey; /* Key of an existing row */ + KVSize nKey; /* Size of the existing row key */ + int n; /* Number of bytes decoded */ v = 0; - res = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - if( NEVER(pC->pCursor==0) ){ - /* The zero initialization above is all that is needed */ - }else{ - /* The next rowid or record number (different terms for the same - ** thing) is obtained in a two-step algorithm. - ** - ** First we attempt to find the largest existing rowid and add one - ** to that. But if the largest existing rowid is already the maximum - ** positive integer, we have to fall through to the second - ** probabilistic algorithm - ** - ** The second algorithm is to select a rowid at random and see if - ** it already exists in the table. If it does not exist, we have - ** succeeded. If the random rowid does exist, we select a new one - ** and try again, up to 100 times. - */ - assert( pC->isTable ); - -#ifdef SQLITE_32BIT_ROWID -# define MAX_ROWID 0x7fffffff -#else - /* Some compilers complain about constants of the form 0x7fffffffffffffff. - ** Others complain about 0x7ffffffffffffffffLL. The following macro seems - ** to provide the constant while making all compilers happy. - */ -# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) -#endif - - if( !pC->useRandomRowid ){ - v = sqlite4BtreeGetCachedRowid(pC->pCursor); - if( v==0 ){ - rc = sqlite4BtreeLast(pC->pCursor, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( res ){ - v = 1; /* IMP: R-61914-48074 */ - }else{ - assert( sqlite4BtreeCursorIsValid(pC->pCursor) ); - rc = sqlite4BtreeKeySize(pC->pCursor, &v); - assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ - if( v==MAX_ROWID ){ - pC->useRandomRowid = 1; - }else{ - v++; /* IMP: R-29538-34987 */ - } - } - } - -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( pOp->p3 ){ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3>0 ); - if( p->pFrame ){ - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=pFrame->nMem ); - pMem = &pFrame->aMem[pOp->p3]; - }else{ - /* 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); - sqlite4VdbeMemIntegerify(pMem); - assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ - rc = SQLITE_FULL; /* IMP: R-12275-61338 */ - goto abort_due_to_error; - } - if( vu.i+1 ){ - v = pMem->u.i + 1; - } - pMem->u.i = v; - } -#endif - - sqlite4BtreeSetCachedRowid(pC->pCursor, vuseRandomRowid ){ - /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the - ** largest possible integer (9223372036854775807) then the database - ** 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 = lastRowid; - v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - v++; /* ensure non-zero */ - cnt = 0; - while( ((rc = sqlite4BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, - 0, &res))==SQLITE_OK) - && (res==0) - && (++cnt<100)){ - /* collision - try another random rowid */ - sqlite4_randomness(sizeof(v), &v); - if( cnt<5 ){ - /* try "small" random rowids for the initial attempts */ - v &= 0xffffff; - }else{ - v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - } - 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; - pC->cacheStatus = CACHE_STALE; - } - pOut->u.i = v; + + /* Some compilers complain about constants of the form 0x7fffffffffffffff. + ** Others complain about 0x7ffffffffffffffffLL. The following macro seems + ** to provide the constant while making all compilers happy. + */ +# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) + + /* The next rowid or record number (different terms for the same + ** thing) is obtained in a two-step algorithm. + ** + ** First we attempt to find the largest existing rowid and add one + ** to that. But if the largest existing rowid is already the maximum + ** positive integer, we have to fall through to the second + ** probabilistic algorithm + ** + ** The second algorithm is to select a rowid at random and see if + ** it already exists in the table. If it does not exist, we have + ** succeeded. If the random rowid does exist, we select a new one + ** and try again, up to 100 times. + */ + assert( pC->isTable ); + + rc = sqlite4VdbeSeekEnd(pC, -1); + if( rc==SQLITE_NOTFOUND ){ + v = 0; + rc = SQLITE_OK; + }else if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + n = sqlite4GetVarint64(aKey, nKey, &v); + if( n==0 ) rc = SQLITE_CORRUPT; + if( v!=pC->iRoot ) rc = SQLITE_CORRUPT; + } + if( rc==SQLITE_OK ){ + n = sqlite4VdbeDecodeIntKey(&aKey[n], nKey-n, &v); + if( n==0 ) rc = SQLITE_CORRUPT; + } + }else{ + break; + } + pOut->flags = MEM_Int; + pOut->u.i = v+1; break; } /* Opcode: Insert P1 P2 P3 P4 P5 ** @@ -3991,23 +3143,21 @@ case OP_InsertInt: { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ i64 iKey; /* The integer ROWID or key for the record to be inserted */ VdbeCursor *pC; /* Cursor to table into which insert is written */ - int nZero; /* Number of zero-bytes to append */ - int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ + int n; + KVByteArray aKey[24]; pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); - assert( pC->pseudoTableReg==0 ); assert( pC->isTable ); REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; @@ -4026,24 +3176,14 @@ pData->z = 0; pData->n = 0; }else{ assert( pData->flags & (MEM_Blob|MEM_Str) ); } - seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); - if( pData->flags & MEM_Zero ){ - nZero = pData->u.nZero; - }else{ - nZero = 0; - } - sqlite4BtreeSetCachedRowid(pC->pCursor, 0); - rc = sqlite4BtreeInsert(pC->pCursor, 0, iKey, - pData->z, pData->n, nZero, - pOp->p5 & OPFLAG_APPEND, seekResult - ); - pC->rowidIsValid = 0; - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; + n = sqlite4PutVarint64(aKey, pC->iRoot); + n += sqlite4VdbeEncodeIntKey(&aKey[n], iKey); + rc = sqlite4KVStoreReplace(pC->pKVCur->pStore, aKey, n, + (const KVByteArray*)pData->z, pData->n); /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ zDb = db->aDb[pC->iDb].zName; zTbl = pOp->p4.z; @@ -4081,11 +3221,10 @@ iKey = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ /* If the update-hook will be invoked, set iKey to the rowid of the ** row being deleted. */ if( db->xUpdateCallback && pOp->p4.z ){ @@ -4092,24 +3231,11 @@ assert( pC->isTable ); assert( pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ iKey = pC->lastRowid; } - /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or - ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor pC is always pointing - ** to the row to be deleted and the sqlite4VdbeCursorMoveto() operation - ** below is always a no-op and cannot fail. We will run it anyhow, though, - ** to guard against future changes to the code generator. - **/ - assert( pC->deferredMoveto==0 ); - rc = sqlite4VdbeCursorMoveto(pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - - sqlite4BtreeSetCachedRowid(pC->pCursor, 0); - rc = sqlite4BtreeDelete(pC->pCursor); - pC->cacheStatus = CACHE_STALE; + rc = sqlite4KVCursorDelete(pC->pKVCur); /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ const char *zDb = db->aDb[pC->iDb].zName; const char *zTbl = pOp->p4.z; @@ -4117,10 +3243,11 @@ assert( pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } + /* Opcode: ResetCount * * * * * ** ** The value of the change counter is copied to the database handle ** change counter (returned by subsequent calls to sqlite4_changes()). ** Then the VMs internal change counter resets to 0. @@ -4142,10 +3269,11 @@ case OP_SorterCompare: { VdbeCursor *pC; int res; pC = p->apCsr[pOp->p1]; + assert( pC->iRoot>0 ); assert( isSorter(pC) ); pIn3 = &aMem[pOp->p3]; rc = sqlite4VdbeSorterCompare(pC, pIn3, &res); if( res ){ pc = pOp->p2-1; @@ -4156,14 +3284,15 @@ /* Opcode: SorterData P1 P2 * * * ** ** Write into register P2 the current sorter data for sorter cursor P1. */ case OP_SorterData: { - VdbeCursor *pC; -#ifndef SQLITE_OMIT_MERGE_SORT + VdbeCursor *pC; pOut = &aMem[pOp->p2]; pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +#ifndef SQLITE_OMIT_MERGE_SORT assert( pC->isSorter ); rc = sqlite4VdbeSorterRowkey(pC, pOut); #else pOp->opcode = OP_RowKey; pc--; @@ -4192,13 +3321,13 @@ ** of a real table, not a pseudo-table. */ case OP_RowKey: case OP_RowData: { VdbeCursor *pC; - BtCursor *pCrsr; - u32 n; - i64 n64; + KVCursor *pCrsr; + const KVByteArray *pData; + KVSize nData; pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ @@ -4208,48 +3337,22 @@ assert( pC->isIndex || pOp->opcode==OP_RowData ); assert( pC!=0 ); assert( pC->nullRow==0 ); assert( pC->pseudoTableReg==0 ); assert( !pC->isSorter ); - assert( pC->pCursor!=0 ); - pCrsr = pC->pCursor; - assert( sqlite4BtreeCursorIsValid(pCrsr) ); - - /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or - ** OP_Rewind/Op_Next with no intervening instructions that might invalidate - ** the cursor. Hence the following sqlite4VdbeCursorMoveto() call is always - ** a no-op and can never fail. But we leave it in place as a safety. - */ - assert( pC->deferredMoveto==0 ); - rc = sqlite4VdbeCursorMoveto(pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - - if( pC->isIndex ){ - assert( !pC->isTable ); - VVA_ONLY(rc =) sqlite4BtreeKeySize(pCrsr, &n64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - n = (u32)n64; - }else{ - VVA_ONLY(rc =) sqlite4BtreeDataSize(pCrsr, &n); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - } - if( sqlite4VdbeMemGrow(pOut, n, 0) ){ - goto no_mem; - } - pOut->n = n; - MemSetTypeFlag(pOut, MEM_Blob); - if( pC->isIndex ){ - rc = sqlite4BtreeKey(pCrsr, 0, n, pOut->z); - }else{ - rc = sqlite4BtreeData(pCrsr, 0, n, pOut->z); - } + assert( pC->pKVCur!=0 ); + pCrsr = pC->pKVCur; + + if( pOp->opcode==OP_RowKey ){ + rc = sqlite4KVCursorKey(pCrsr, &pData, &nData); + }else{ + rc = sqlite4KVCursorData(pCrsr, 0, -1, &pData, &nData); + } + if( rc==SQLITE_OK && nData>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } + sqlite4VdbeMemSetStr(pOut, (const char*)pData, nData, 0, SQLITE_DYNAMIC); pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -4265,37 +3368,35 @@ case OP_Rowid: { /* out2-prerelease */ VdbeCursor *pC; i64 v; sqlite4_vtab *pVtab; const sqlite4_module *pModule; + const KVByteArray *aKey; + KVSize nKey; + int n; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); if( pC->nullRow ){ pOut->flags = MEM_Null; break; - }else if( pC->deferredMoveto ){ - v = pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( pC->pVtabCursor ){ pVtab = pC->pVtabCursor->pVtab; pModule = pVtab->pModule; assert( pModule->xRowid ); rc = pModule->xRowid(pC->pVtabCursor, &v); importVtabErrMsg(p, pVtab); #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ - assert( pC->pCursor!=0 ); - rc = sqlite4VdbeCursorMoveto(pC); - if( rc ) goto abort_due_to_error; - if( pC->rowidIsValid ){ - v = pC->lastRowid; - }else{ - rc = sqlite4BtreeKeySize(pC->pCursor, &v); - assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ + rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + n = sqlite4GetVarint64(aKey, nKey, (sqlite4_uint64*)&v); + n = sqlite4VdbeDecodeIntKey(&aKey[n], nKey-n, &v); + if( n==0 ) rc = SQLITE_CORRUPT; } } pOut->u.i = v; break; } @@ -4312,14 +3413,10 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pC->nullRow = 1; pC->rowidIsValid = 0; - assert( pC->pCursor || pC->pVtabCursor ); - if( pC->pCursor ){ - sqlite4BtreeClearCursor(pC->pCursor); - } break; } /* Opcode: Last P1 P2 * * * ** @@ -4329,27 +3426,18 @@ ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Last: { /* jump */ VdbeCursor *pC; - BtCursor *pCrsr; - int res; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - pCrsr = pC->pCursor; - res = 0; - if( ALWAYS(pCrsr!=0) ){ - rc = sqlite4BtreeLast(pCrsr, &res); - } - pC->nullRow = (u8)res; - pC->deferredMoveto = 0; - pC->rowidIsValid = 0; - pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && res ){ - pc = pOp->p2 - 1; + rc = sqlite4VdbeSeekEnd(pC, -1); + if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; + if( pOp->p2 ) pc = pOp->p2 - 1; } break; } @@ -4385,32 +3473,31 @@ ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Rewind: { /* jump */ VdbeCursor *pC; - BtCursor *pCrsr; - int res; + int doJump; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isSorter==(pOp->opcode==OP_SorterSort) ); - res = 1; + doJump = 1; if( isSorter(pC) ){ - rc = sqlite4VdbeSorterRewind(db, pC, &res); + rc = sqlite4VdbeSorterRewind(db, pC, &doJump); }else{ - pCrsr = pC->pCursor; - assert( pCrsr ); - rc = sqlite4BtreeFirst(pCrsr, &res); - pC->atFirst = res==0 ?1:0; - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - pC->rowidIsValid = 0; - } - pC->nullRow = (u8)res; + rc = sqlite4VdbeSeekEnd(pC, +1); + if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; + doJump = 1; + }else{ + doJump = 0; + } + } + pC->nullRow = (u8)doJump; assert( pOp->p2>0 && pOp->p2nOp ); - if( res ){ + if( doJump ){ pc = pOp->p2 - 1; } break; } @@ -4422,11 +3509,11 @@ ** jump immediately to P2. ** ** The P1 cursor must be for a real table, not a pseudo-table. ** ** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlite4BtreeNext(). +** sqlite4VdbeNext(). ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** ** See also: Prev @@ -4439,11 +3526,11 @@ ** jump immediately to P2. ** ** The P1 cursor must be for a real table, not a pseudo-table. ** ** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlite4BtreePrevious(). +** sqlite4VdbePrevious(). ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ case OP_SorterNext: /* jump */ @@ -4464,26 +3551,27 @@ } assert( pC->isSorter==(pOp->opcode==OP_SorterNext) ); if( isSorter(pC) ){ assert( pOp->opcode==OP_SorterNext ); rc = sqlite4VdbeSorterNext(db, pC, &res); + if( rc==SQLITE_OK && res ) rc = SQLITE_NOTFOUND; }else{ res = 1; - assert( pC->deferredMoveto==0 ); - assert( pC->pCursor ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite4BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite4BtreePrevious ); - rc = pOp->p4.xAdvance(pC->pCursor, &res); - } - pC->nullRow = (u8)res; - pC->cacheStatus = CACHE_STALE; - if( res==0 ){ + assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite4VdbeNext ); + assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite4VdbePrevious ); + rc = pOp->p4.xAdvance(pC); + } + if( rc==SQLITE_OK ){ pc = pOp->p2 - 1; if( pOp->p5 ) p->aCounter[pOp->p5-1]++; + pC->nullRow = 0; #ifdef SQLITE_TEST sqlite4_search_count++; #endif + }else if( rc==SQLITE_NOTFOUND ){ + pC->nullRow = 1; + rc = SQLITE_OK; } pC->rowidIsValid = 0; break; } @@ -4497,45 +3585,42 @@ ** insert is likely to be an append. ** ** This instruction only works for indices. The equivalent instruction ** for tables is OP_Insert. */ -case OP_SorterInsert: /* in2 */ -#ifdef SQLITE_OMIT_MERGE_SORT - pOp->opcode = OP_IdxInsert; -#endif -case OP_IdxInsert: { /* in2 */ +case OP_SorterInsert: { /* in2 */ VdbeCursor *pC; - BtCursor *pCrsr; - int nKey; - const char *zKey; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) ); pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); - pCrsr = pC->pCursor; - if( ALWAYS(pCrsr!=0) ){ - assert( pC->isTable==0 ); - rc = ExpandBlob(pIn2); - if( rc==SQLITE_OK ){ - if( isSorter(pC) ){ - rc = sqlite4VdbeSorterWrite(db, pC, pIn2); - }else{ - nKey = pIn2->n; - zKey = pIn2->z; - rc = sqlite4BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, - ((pOp->p5 & OPFLAG_APPENDBIAS) ? 1 : 0), - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) - ); - assert( pC->deferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - } - } - } + assert( pC->isTable==0 ); + rc = ExpandBlob(pIn2); + if( rc==SQLITE_OK ){ + rc = sqlite4VdbeSorterWrite(db, pC, pIn2); + } + break; +} + + +/* Opcode: IdxInsert P1 P2 * * P5 +** +** Register P2 holds an SQL index key made using the +** MakeRecord instructions. This opcode writes that key +** into the index P1. Data for the entry is nil. +** +** P3 is a flag that provides a hint to the b-tree layer that this +** insert is likely to be an append. +** +** This instruction only works for indices. The equivalent instruction +** for tables is OP_Insert. +*/ +case OP_IdxInsert: { /* in2 */ + assert( 0 ); break; } /* Opcode: IdxDelete P1 P2 P3 * * ** @@ -4542,36 +3627,11 @@ ** The content of P3 registers starting at register P2 form ** an unpacked index key. This opcode removes that entry from the ** index opened by cursor P1. */ case OP_IdxDelete: { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - UnpackedRecord r; - - assert( pOp->p3>0 ); - assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pCrsr = pC->pCursor; - if( ALWAYS(pCrsr!=0) ){ - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p3; - r.flags = 0; - r.aMem = &aMem[pOp->p2]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ideferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - } + assert( 0 ); break; } /* Opcode: IdxRowid P1 P2 * * * ** @@ -4580,33 +3640,11 @@ ** the rowid of the table entry to which this index entry points. ** ** See also: Rowid, MakeRecord. */ case OP_IdxRowid: { /* out2-prerelease */ - BtCursor *pCrsr; - VdbeCursor *pC; - i64 rowid; - - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pCrsr = pC->pCursor; - pOut->flags = MEM_Null; - if( ALWAYS(pCrsr!=0) ){ - rc = sqlite4VdbeCursorMoveto(pC); - if( NEVER(rc) ) goto abort_due_to_error; - assert( pC->deferredMoveto==0 ); - assert( pC->isTable==0 ); - if( !pC->nullRow ){ - rc = sqlite4VdbeIdxRowid(db, pCrsr, &rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pOut->u.i = rowid; - pOut->flags = MEM_Int; - } - } + assert( 0 ); break; } /* Opcode: IdxGE P1 P2 P3 P4 P5 ** @@ -4634,44 +3672,11 @@ ** If P5 is non-zero then the key value is increased by an epsilon prior ** to the comparison. This makes the opcode work like IdxLE. */ case OP_IdxLT: /* jump */ case OP_IdxGE: { /* jump */ - VdbeCursor *pC; - int res; - UnpackedRecord r; - - assert( pOp->p1>=0 && pOp->p1nCursor ); - 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 ); - assert( pOp->p4type==P4_INT32 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; - if( pOp->p5 ){ - r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; - }else{ - r.flags = UNPACKED_PREFIX_MATCH; - } - r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; iopcode==OP_IdxLT ){ - res = -res; - }else{ - assert( pOp->opcode==OP_IdxGE ); - res++; - } - if( res>0 ){ - pc = pOp->p2 - 1 ; - } - } + assert( 0 ); break; } /* Opcode: Destroy P1 P2 P3 * * ** @@ -4683,36 +3688,11 @@ ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** See also: Clear */ case OP_Destroy: { /* out2-prerelease */ - int iMoved; - int iCnt; - Vdbe *pVdbe; - int iDb; -#ifndef SQLITE_OMIT_VIRTUALTABLE - iCnt = 0; - for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){ - if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 ){ - iCnt++; - } - } -#else - iCnt = db->activeVdbeCnt; -#endif - pOut->flags = MEM_Null; - if( iCnt>1 ){ - rc = SQLITE_LOCKED; - p->errorAction = OE_Abort; - }else{ - iDb = pOp->p3; - assert( iCnt==1 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[iDb].pBt, pOp->p1, &iMoved); - pOut->flags = MEM_Int; - pOut->u.i = iMoved; - } + assert( 0 ); break; } /* Opcode: Clear P1 P2 P3 ** @@ -4731,25 +3711,11 @@ ** also incremented by the number of rows in the table being cleared. ** ** See also: Destroy */ case OP_Clear: { - int nChange; - - nChange = 0; - assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); - rc = sqlite4BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) - ); - 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; - } - } + assert( 0 ); break; } /* Opcode: CreateTable P1 P2 * * * ** @@ -4773,27 +3739,45 @@ ** ** See documentation on OP_CreateTable for additional information. */ case OP_CreateIndex: /* out2-prerelease */ case OP_CreateTable: { /* out2-prerelease */ - int pgno; - int flags; + sqlite4_uint64 iTabno; Db *pDb; + KVCursor *pCur; + const KVByteArray *aKey; + KVSize nKey; + int n; + KVByteArray aProbe[12]; - pgno = 0; + iTabno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - if( pOp->opcode==OP_CreateTable ){ - /* flags = BTREE_INTKEY; */ - flags = BTREE_INTKEY; + memset(aProbe, 0xff, 9); + rc = sqlite4KVStoreOpenCursor(pDb->pKV, &pCur); + if( rc ) break; + rc = sqlite4KVCursorSeek(pCur, aProbe, 9, -1); + if( rc==SQLITE_OK ){ + sqlite4KVCursorClose(pCur); + rc = SQLITE_CORRUPT; + break; + } + if( rc==SQLITE_NOTFOUND ){ + iTabno = 2; + n = 1; + rc = SQLITE_OK; + }else if( rc==SQLITE_INEXACT ){ + rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); + n = sqlite4GetVarint64(aKey, nKey, &iTabno); }else{ - flags = BTREE_BLOBKEY; + break; + } + sqlite4KVCursorClose(pCur); + if( n==0 ){ + rc = SQLITE_CORRUPT; } - rc = sqlite4BtreeCreateTable(pDb->pBt, &pgno, flags); - pOut->u.i = pgno; + pOut->u.i = iTabno; break; } /* Opcode: ParseSchema P1 * * P4 * ** @@ -4807,20 +3791,10 @@ int iDb; const char *zMaster; char *zSql; InitData initData; - /* Any prepared statement that invokes this opcode will hold mutexes - ** on every btree. This is a prerequisite for invoking - ** sqlite4InitCallback(). - */ -#ifdef SQLITE_DEBUG - for(iDb=0; iDbnDb; iDb++){ - assert( iDb==1 || sqlite4BtreeHoldsMutex(db->aDb[iDb].pBt) ); - } -#endif - iDb = pOp->p1; assert( iDb>=0 && iDbnDb ); assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { zMaster = SCHEMA_TABLE(iDb); @@ -4920,46 +3894,10 @@ ** file, not the main database file. ** ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { - int nRoot; /* Number of tables to check. (Number of root pages.) */ - int *aRoot; /* Array of rootpage numbers for tables to be checked */ - int j; /* Loop counter */ - int nErr; /* Number of errors reported */ - char *z; /* Text of the error report */ - Mem *pnErr; /* Register keeping track of errors remaining */ - - nRoot = pOp->p2; - assert( nRoot>0 ); - aRoot = sqlite4DbMallocRaw(db, sizeof(int)*(nRoot+1) ); - if( aRoot==0 ) goto no_mem; - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pnErr = &aMem[pOp->p3]; - assert( (pnErr->flags & MEM_Int)!=0 ); - assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &aMem[pOp->p1]; - for(j=0; jp5nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p5))!=0 ); - z = sqlite4BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot, - (int)pnErr->u.i, &nErr); - sqlite4DbFree(db, aRoot); - pnErr->u.i -= nErr; - sqlite4VdbeMemSetNull(pIn1); - if( nErr==0 ){ - assert( z==0 ); - }else if( z==0 ){ - goto no_mem; - }else{ - sqlite4VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite4_free); - } - UPDATE_MAX_BLOBSIZE(pIn1); - sqlite4VdbeChangeEncoding(pIn1, encoding); break; } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* Opcode: RowSetAdd P1 P2 * * * @@ -5422,45 +4360,10 @@ ** If changing into or out of WAL mode the procedure is more complicated. ** ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { /* out2-prerelease */ - Btree *pBt; /* Btree to change journal mode of */ - Pager *pPager; /* Pager associated with pBt */ - int eNew; /* New journal mode */ - int eOld; /* The old journal mode */ - const char *zFilename; /* Name of database file for pPager */ - - eNew = pOp->p3; - assert( eNew==PAGER_JOURNALMODE_DELETE - || eNew==PAGER_JOURNALMODE_TRUNCATE - || eNew==PAGER_JOURNALMODE_PERSIST - || eNew==PAGER_JOURNALMODE_OFF - || eNew==PAGER_JOURNALMODE_MEMORY - || eNew==PAGER_JOURNALMODE_WAL - || eNew==PAGER_JOURNALMODE_QUERY - ); - assert( pOp->p1>=0 && pOp->p1nDb ); - - pBt = db->aDb[pOp->p1].pBt; - pPager = sqlite4BtreePager(pBt); - eOld = sqlite4PagerGetJournalMode(pPager); - if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; - if( !sqlite4PagerOkToChangeJournalMode(pPager) ) eNew = eOld; - - - if( rc ){ - eNew = eOld; - } - eNew = sqlite4PagerSetJournalMode(pPager, eNew); - - pOut = &aMem[pOp->p2]; - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlite4JournalModename(eNew); - pOut->n = sqlite4Strlen30(pOut->z); - pOut->enc = SQLITE_UTF8; - sqlite4VdbeChangeEncoding(pOut, encoding); break; }; #endif /* SQLITE_OMIT_PRAGMA */ @@ -5496,22 +4399,10 @@ ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { - u8 isWriteLock = (u8)pOp->p3; - if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ - int p1 = pOp->p1; - assert( p1>=0 && p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[p1].pBt, pOp->p2, isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite4SetString(&p->zErrMsg, db, "database table is locked: %s", z); - } - } break; } #endif /* SQLITE_OMIT_SHARED_CACHE */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -5980,11 +4871,10 @@ /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: db->lastRowid = lastRowid; - sqlite4VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH ** is encountered. */ Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -31,10 +31,11 @@ ** for the VdbeOp definition. */ typedef struct VdbeFunc VdbeFunc; typedef struct Mem Mem; typedef struct SubProgram SubProgram; +typedef struct VdbeCursor VdbeCursor; /* ** A single instruction of the virtual machine has an opcode ** and as many as three operands. The instruction is recorded ** as an instance of the following structure: @@ -59,11 +60,11 @@ Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ - int (*xAdvance)(BtCursor *, int *); + int (*xAdvance)(VdbeCursor*); } p4; #ifdef SQLITE_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE @@ -181,11 +182,11 @@ void sqlite4VdbeChangeP3(Vdbe*, u32 addr, int P3); void sqlite4VdbeChangeP5(Vdbe*, u8 P5); void sqlite4VdbeJumpHere(Vdbe*, int addr); void sqlite4VdbeChangeToNoop(Vdbe*, int addr); void sqlite4VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); -void sqlite4VdbeUsesBtree(Vdbe*, int); +void sqlite4VdbeUsesStorage(Vdbe*, int); VdbeOp *sqlite4VdbeGetOp(Vdbe*, int); int sqlite4VdbeMakeLabel(Vdbe*); void sqlite4VdbeRunOnlyOnce(Vdbe*); void sqlite4VdbeDelete(Vdbe*); void sqlite4VdbeDeleteObject(sqlite4*,Vdbe*); Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -38,23 +38,21 @@ /* Opaque type used by vdbecodec.c */ typedef struct ValueDecoder ValueDecoder; /* -** A cursor is a pointer into a single BTree within a database file. -** The cursor can seek to a BTree entry with a particular key, or -** loop over all entries of the Btree. You can also insert new BTree +** A cursor is a pointer into a single database. +** The cursor can seek to an entry with a particular key, or +** loop over all entries. You can also insert new ** entries or retrieve the key or data from the entry that the cursor ** is currently pointing to. ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. */ struct VdbeCursor { - BtCursor *pCursor; /* The cursor structure of the backend */ KVCursor *pKVCur; /* The cursor structure of the backend */ - Btree *pBt; /* Separate file holding temporary table */ KVStore *pTmpKV; /* Separate file holding a temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */ int iRoot; /* Root page of the table */ int pseudoTableReg; /* Register holding pseudotable content. */ @@ -62,40 +60,24 @@ 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 */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ - Bool deferredMoveto; /* A call to sqlite4BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ Bool isSorter; /* True if a new-style sorter */ sqlite4_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite4_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ - i64 movetoTarget; /* Argument to the deferred sqlite4BtreeMoveto() */ + i64 movetoTarget; /* Argument to the deferred move-to */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ - /* Result of last sqlite4BtreeMoveto() done by an OP_NotExists or + /* Result of last sqlite4-Moveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ int seekResult; - - /* Cached information about the header for the data record that the - ** cursor is currently pointing to. Only valid if cacheStatus matches - ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of - ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that - ** the cache is out of date. - ** - ** aRow might point to (ephemeral) data for the current row, or it might - ** be NULL. - */ - u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int payloadSize; /* Total number of bytes in the record */ - u32 *aType; /* Type values for all entries in the record */ - u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ }; typedef struct VdbeCursor VdbeCursor; /* ** When a sub-program is executed (OP_Program), a structure of this type @@ -204,14 +186,10 @@ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #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) \ @@ -324,17 +302,15 @@ u8 changeCntOn; /* True to update the change-counter */ u8 expired; /* True if the VM needs to be recompiled */ u8 runOnlyOnce; /* Automatically expire on reset */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 inVtabMethod; /* See comments above */ - u8 usesStmtJournal; /* True if uses a statement journal */ + u8 needSavepoint; /* True if a change might abort and needs savepoint */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ - yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ - yDbMask lockMask; /* Subset of btreeMask that requires a lock */ - int iStatement; /* Statement number (or 0 if has not opened stmt) */ + yDbMask stmtTransMask; /* db->aDb[] entries that have a subtransaction */ int aCounter[3]; /* Counters used by sqlite4_stmt_status() */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif i64 nFkConstraint; /* Number of imm. FK constraints this VM */ @@ -368,11 +344,10 @@ /* ** Function prototypes */ void sqlite4VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); -int sqlite4VdbeCursorMoveto(VdbeCursor*); #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) void sqlite4VdbePrintOp(FILE*, int, Op*); #endif u32 sqlite4VdbeSerialTypeLen(u32); u32 sqlite4VdbeSerialType(Mem*, int); @@ -406,15 +381,15 @@ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence information */ u8 **pzOut, /* Write the resulting key here */ - int *pnOut /* Number of bytes in the key */ + int *pnOut, /* Number of bytes in the key */ + int *pnShort /* Number of bytes omitting primary key */ ); -int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); -int sqlite4VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*); -int sqlite4VdbeIdxRowid(sqlite4*, BtCursor *, i64 *); +int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v); +int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*); int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*); int sqlite4VdbeExec(Vdbe*); int sqlite4VdbeList(Vdbe*); int sqlite4VdbeHalt(Vdbe*); int sqlite4VdbeChangeEncoding(Mem *, int); @@ -439,11 +414,11 @@ int sqlite4VdbeMemIntegerify(Mem*); double sqlite4VdbeRealValue(Mem*); void sqlite4VdbeIntegerAffinity(Mem*); int sqlite4VdbeMemRealify(Mem*); int sqlite4VdbeMemNumerify(Mem*); -int sqlite4VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); + void sqlite4VdbeMemRelease(Mem *p); void sqlite4VdbeMemReleaseExternal(Mem *p); #define VdbeMemRelease(X) \ if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \ sqlite4VdbeMemReleaseExternal(X); @@ -453,10 +428,13 @@ int sqlite4VdbeCloseStatement(Vdbe *, int); void sqlite4VdbeFrameDelete(VdbeFrame*); int sqlite4VdbeFrameRestore(VdbeFrame *); void sqlite4VdbeMemStoreType(Mem *pMem); int sqlite4VdbeTransferError(Vdbe *p); +int sqlite4VdbeSeekEnd(VdbeCursor*, int); +int sqlite4VdbeNext(VdbeCursor*); +int sqlite4VdbePrevious(VdbeCursor*); #ifdef SQLITE_OMIT_MERGE_SORT # define sqlite4VdbeSorterInit(Y,Z) SQLITE_OK # define sqlite4VdbeSorterWrite(X,Y,Z) SQLITE_OK # define sqlite4VdbeSorterClose(Y,Z) @@ -472,18 +450,10 @@ int sqlite4VdbeSorterRewind(sqlite4 *, VdbeCursor *, int *); int sqlite4VdbeSorterWrite(sqlite4 *, VdbeCursor *, Mem *); int sqlite4VdbeSorterCompare(VdbeCursor *, Mem *, int *); #endif -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 - void sqlite4VdbeEnter(Vdbe*); - void sqlite4VdbeLeave(Vdbe*); -#else -# define sqlite4VdbeEnter(X) -# define sqlite4VdbeLeave(X) -#endif - #ifdef SQLITE_DEBUG void sqlite4VdbeMemAboutToChange(Vdbe*,Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY @@ -497,14 +467,10 @@ void sqlite4VdbePrintSql(Vdbe*); void sqlite4VdbeMemPrettyPrint(Mem *pMem, char *zBuf); #endif int sqlite4VdbeMemHandleBom(Mem *pMem); -#ifndef SQLITE_OMIT_INCRBLOB - int sqlite4VdbeMemExpandBlob(Mem *); - #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite4VdbeMemExpandBlob(P):0) -#else - #define sqlite4VdbeMemExpandBlob(x) SQLITE_OK - #define ExpandBlob(P) SQLITE_OK -#endif + +#define sqlite4VdbeMemExpandBlob(x) SQLITE_OK +#define ExpandBlob(P) SQLITE_OK #endif /* !defined(_VDBEINT_H_) */ Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -306,31 +306,10 @@ sqlite4VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; pCtx->s.db->mallocFailed = 1; } -/* -** This function is called after a transaction has been committed. It -** invokes callbacks registered with sqlite4_wal_hook() as required. -*/ -static int doWalCallbacks(sqlite4 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_WAL - int i; - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - int nEntry = sqlite4PagerWalCallback(sqlite4BtreePager(pBt)); - if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ - rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); - } - } - } -#endif - return rc; -} - /* ** Execute the statement pStmt, either until a row of data is ready, the ** statement is completely executed or an error occurs. ** ** This routine implements the bulk of the logic behind the sqlite_step() @@ -423,18 +402,10 @@ sqlite4OsCurrentTimeInt64(db->pVfs, &iNow); db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000); } #endif - if( rc==SQLITE_DONE ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; - } - } - db->errCode = rc; if( SQLITE_NOMEM==sqlite4ApiExit(p->db, p->rc) ){ p->rc = SQLITE_NOMEM; } end_of_step: Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -204,11 +204,11 @@ */ void sqlite4VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ int j; int addr = sqlite4VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0); sqlite4VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC); - for(j=0; jdb->nDb; j++) sqlite4VdbeUsesBtree(p, j); + for(j=0; jdb->nDb; j++) sqlite4VdbeUsesStorage(p, j); } /* ** Add an opcode that includes the p4 value as an integer. */ @@ -433,14 +433,14 @@ assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; #endif }else if( opcode==OP_Next || opcode==OP_SorterNext ){ - pOp->p4.xAdvance = sqlite4BtreeNext; + pOp->p4.xAdvance = sqlite4VdbeNext; pOp->p4type = P4_ADVANCE; }else if( opcode==OP_Prev ){ - pOp->p4.xAdvance = sqlite4BtreePrevious; + pOp->p4.xAdvance = sqlite4VdbePrevious; pOp->p4type = P4_ADVANCE; } if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){ assert( -1-pOp->p2nLabel ); @@ -474,13 +474,10 @@ */ VdbeOp *sqlite4VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ VdbeOp *aOp = p->aOp; assert( aOp && !p->db->mallocFailed ); - /* Check that sqlite4VdbeUsesBtree() was not called on this VM */ - assert( p->btreeMask==0 ); - resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; p->aOp = 0; return aOp; } @@ -959,87 +956,16 @@ return zP4; } #endif /* -** Declare to the Vdbe that the BTree object at db->aDb[i] is used. -** -** The prepared statements need to know in advance the complete set of -** attached databases that will be use. A mask of these databases -** is maintained in p->btreeMask. The p->lockMask value is the subset of -** p->btreeMask of databases that will require a lock. +** Declare to the Vdbe that the database at db->aDb[i] is used. */ -void sqlite4VdbeUsesBtree(Vdbe *p, int i){ +void sqlite4VdbeUsesStorage(Vdbe *p, int i){ assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); - assert( i<(int)sizeof(p->btreeMask)*8 ); - p->btreeMask |= ((yDbMask)1)<db->aDb[i].pBt) ){ - p->lockMask |= ((yDbMask)1)<0 -/* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it also -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. -** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlite4BtreeEnter() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. -** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. -** -** The p->btreeMask field is a bitmask of all btrees that the prepared -** statement p will ever use. Let N be the number of bits in p->btreeMask -** corresponding to btrees that use shared cache. Then the runtime of -** this routine is N*N. But as N is rarely more than 1, this should not -** be a problem. -*/ -void sqlite4VdbeEnter(Vdbe *p){ - int i; - yDbMask mask; - sqlite4 *db; - Db *aDb; - int nDb; - if( p->lockMask==0 ) return; /* The common case */ - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ - sqlite4BtreeEnter(aDb[i].pBt); - } - } -} -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 -/* -** Unlock all of the btrees previously locked by a call to sqlite4VdbeEnter(). -*/ -void sqlite4VdbeLeave(Vdbe *p){ - int i; - yDbMask mask; - sqlite4 *db; - Db *aDb; - int nDb; - if( p->lockMask==0 ) return; /* The common case */ - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ - sqlite4BtreeLeave(aDb[i].pBt); - } - } -} -#endif +} + #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* ** Print a single opcode. This routine is used for debugging only. */ @@ -1433,11 +1359,11 @@ p->errorAction = OE_Abort; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; p->cacheCtr = 1; p->minWriteFileFormat = 255; - p->iStatement = 0; + p->stmtTransMask = 0; p->nFkConstraint = 0; #ifdef VDBE_PROFILE for(i=0; inOp; i++){ p->aOp[i].cnt = 0; p->aOp[i].cycles = 0; @@ -1507,11 +1433,11 @@ */ zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */ zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */ resolveP2Values(p, &nArg); - p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); + p->needSavepoint = (u8)(pParse->isMultiWrite && pParse->mayAbort); if( pParse->explain && nMem<10 ){ nMem = 10; } memset(zCsr, 0, zEnd-zCsr); zCsr += (zCsr - (u8*)0)&7; @@ -1577,19 +1503,16 @@ void sqlite4VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx==0 ){ return; } sqlite4VdbeSorterClose(p->db, pCx); - if( pCx->pBt ){ - sqlite4BtreeClose(pCx->pBt); - sqlite4KVStoreClose(pCx->pTmpKV); - /* The pCx->pCursor will be close automatically, if it exists, by - ** the call above. */ - }else if( pCx->pCursor ){ - sqlite4BtreeCloseCursor(pCx->pCursor); + if( pCx->pKVCur ){ sqlite4KVCursorClose(pCx->pKVCur); } + if( pCx->pTmpKV ){ + sqlite4KVStoreClose(pCx->pTmpKV); + } #ifndef SQLITE_OMIT_VIRTUALTABLE if( pCx->pVtabCursor ){ sqlite4_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite4_module *pModule = pCx->pModule; p->inVtabMethod = 1; @@ -1755,227 +1678,38 @@ */ UNUSED_PARAMETER(p); #endif /* Before doing anything else, call the xSync() callback for any - ** virtual module tables written in this transaction. This has to - ** be done before determining whether a master journal file is - ** required, as an xSync() callback may add an attached database - ** to the transaction. + ** virtual module tables written in this transaction. */ rc = sqlite4VtabSync(db, &p->zErrMsg); - /* This loop determines (a) if the commit hook should be invoked and - ** (b) how many database files have open write transactions, not - ** including the temp database. (b) is important because if more than - ** one database file has an open write transaction, a master journal - ** file is required for an atomic commit. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlite4BtreeIsInTrans(pBt) ){ + /* Phase one commit */ + for(i=0; rc==SQLITE_OK && inDb; i++){ + KVStore *pKV = db->aDb[i].pKV; + if( pKV && pKV->iTransLevel ){ needXcommit = 1; - if( i!=1 ) nTrans++; - rc = sqlite4PagerExclusiveLock(sqlite4BtreePager(pBt)); + rc = sqlite4KVStoreCommitPhaseOne(pKV, 0); } } - if( rc!=SQLITE_OK ){ - return rc; - } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ rc = db->xCommitCallback(db->pCommitArg); if( rc ){ return SQLITE_CONSTRAINT; } } - /* The simple case - no more than one database file (not counting the - ** TEMP database) has a transaction active. There is no need for the - ** master-journal. - ** - ** If the return value of sqlite4BtreeGetFilename() is a zero length - ** string, it means the main database is :memory: or a temp file. In - ** that case we do not support atomic multi-file commits, so use the - ** simple case then too. - */ - if( 0==sqlite4Strlen30(sqlite4BtreeGetFilename(db->aDb[0].pBt)) - || nTrans<=1 - ){ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite4BtreeCommitPhaseOne(pBt, 0); - } - } - - /* Do the commit only if all databases successfully complete phase 1. - ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an - ** IO error while deleting or truncating a journal file. It is unlikely, - ** but could happen. In this case abandon processing and return the error. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite4BtreeCommitPhaseTwo(pBt, 0); - } - } - if( rc==SQLITE_OK ){ - sqlite4VtabCommit(db); - } - } - - /* The complex case - There is a multi-file write-transaction active. - ** This requires a master journal file to ensure the transaction is - ** committed atomicly. - */ -#ifndef SQLITE_OMIT_DISKIO - else{ - sqlite4_vfs *pVfs = db->pVfs; - int needSync = 0; - char *zMaster = 0; /* File-name for the master journal */ - char const *zMainFile = sqlite4BtreeGetFilename(db->aDb[0].pBt); - sqlite4_file *pMaster = 0; - i64 offset = 0; - int res; - int retryCount = 0; - int nMainFile; - - /* Select a master journal file name */ - nMainFile = sqlite4Strlen30(zMainFile); - zMaster = sqlite4MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile); - if( zMaster==0 ) return SQLITE_NOMEM; - do { - u32 iRandom; - if( retryCount ){ - if( retryCount>100 ){ - sqlite4_log(SQLITE_FULL, "MJ delete: %s", zMaster); - sqlite4OsDelete(pVfs, zMaster, 0); - break; - }else if( retryCount==1 ){ - sqlite4_log(SQLITE_FULL, "MJ collide: %s", zMaster); - } - } - retryCount++; - sqlite4_randomness(sizeof(iRandom), &iRandom); - sqlite4_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X", - (iRandom>>8)&0xffffff, iRandom&0xff); - /* The antipenultimate character of the master journal name must - ** be "9" to avoid name collisions when using 8+3 filenames. */ - assert( zMaster[sqlite4Strlen30(zMaster)-3]=='9' ); - sqlite4FileSuffix3(zMainFile, zMaster); - rc = sqlite4OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); - }while( rc==SQLITE_OK && res ); - if( rc==SQLITE_OK ){ - /* Open the master journal. */ - rc = sqlite4OsOpenMalloc(pVfs, zMaster, &pMaster, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 - ); - } - if( rc!=SQLITE_OK ){ - sqlite4DbFree(db, zMaster); - return rc; - } - - /* Write the name of each database file in the transaction into the new - ** master journal file. If an error occurs at this point close - ** and delete the master journal file. All the individual journal files - ** still have 'null' as the master journal pointer, so they will roll - ** back independently if a failure occurs. - */ - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlite4BtreeIsInTrans(pBt) ){ - char const *zFile = sqlite4BtreeGetJournalname(pBt); - if( zFile==0 ){ - continue; /* Ignore TEMP and :memory: databases */ - } - assert( zFile[0]!=0 ); - if( !needSync && !sqlite4BtreeSyncDisabled(pBt) ){ - needSync = 1; - } - rc = sqlite4OsWrite(pMaster, zFile, sqlite4Strlen30(zFile)+1, offset); - offset += sqlite4Strlen30(zFile)+1; - if( rc!=SQLITE_OK ){ - sqlite4OsCloseFree(pMaster); - sqlite4OsDelete(pVfs, zMaster, 0); - sqlite4DbFree(db, zMaster); - return rc; - } - } - } - - /* Sync the master journal file. If the IOCAP_SEQUENTIAL device - ** flag is set this is not required. - */ - if( needSync - && 0==(sqlite4OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) - && SQLITE_OK!=(rc = sqlite4OsSync(pMaster, SQLITE_SYNC_NORMAL)) - ){ - sqlite4OsCloseFree(pMaster); - sqlite4OsDelete(pVfs, zMaster, 0); - sqlite4DbFree(db, zMaster); - return rc; - } - - /* Sync all the db files involved in the transaction. The same call - ** sets the master journal pointer in each individual journal. If - ** an error occurs here, do not delete the master journal file. - ** - ** If the error occurs during the first call to - ** sqlite4BtreeCommitPhaseOne(), then there is a chance that the - ** master journal file will be orphaned. But we cannot delete it, - ** in case the master journal file name was written into the journal - ** file before the failure occurred. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite4BtreeCommitPhaseOne(pBt, zMaster); - } - } - sqlite4OsCloseFree(pMaster); - assert( rc!=SQLITE_BUSY ); - if( rc!=SQLITE_OK ){ - sqlite4DbFree(db, zMaster); - return rc; - } - - /* Delete the master journal file. This commits the transaction. After - ** doing this the directory is synced again before any individual - ** transaction files are deleted. - */ - rc = sqlite4OsDelete(pVfs, zMaster, 1); - sqlite4DbFree(db, zMaster); - zMaster = 0; - if( rc ){ - return rc; - } - - /* All files and directories have already been synced, so the following - ** calls to sqlite4BtreeCommitPhaseTwo() are only closing files and - ** deleting or truncating journals. If something goes wrong while - ** this is happening we don't really care. The integrity of the - ** transaction is already guaranteed, but some stray 'cold' journals - ** may be lying around. Returning an error code won't help matters. - */ - disable_simulated_io_errors(); - sqlite4BeginBenignMalloc(); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - sqlite4BtreeCommitPhaseTwo(pBt, 1); - } - } - sqlite4EndBenignMalloc(); - enable_simulated_io_errors(); - - sqlite4VtabCommit(db); - } -#endif + /* Do phase two of the commit */ + for(i=0; rc==SQLITE_OK && inDb; i++){ + KVStore *pKV = db->aDb[i].pKV; + if( pKV ){ + rc = sqlite4KVStoreCommitPhaseTwo(pKV, 0); + } + } return rc; } /* @@ -2005,36 +1739,10 @@ } #else #define checkActiveVdbeCnt(x) #endif -/* -** For every Btree that in database connection db which -** has been modified, "trip" or invalidate each cursor in -** that Btree might have been modified so that the cursor -** can never be used again. This happens when a rollback -*** occurs. We have to trip all the other cursors, even -** cursor from other VMs in different database connections, -** so that none of them try to use the data at which they -** were pointing and which now may have been changed due -** to the rollback. -** -** Remember that a rollback can delete tables complete and -** reorder rootpages. So it is not sufficient just to save -** the state of the cursor. We have to invalidate the cursor -** so that it is never used again. -*/ -static void invalidateCursorsOnModifiedBtrees(sqlite4 *db){ - int i; - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p && sqlite4BtreeIsInTrans(p) ){ - sqlite4BtreeTripAllCursors(p, SQLITE_ABORT); - } - } -} - /* ** If the Vdbe passed as the first argument opened a statement-transaction, ** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or ** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement ** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the @@ -2042,61 +1750,11 @@ ** ** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. ** Otherwise SQLITE_OK. */ int sqlite4VdbeCloseStatement(Vdbe *p, int eOp){ - sqlite4 *const db = p->db; - int rc = SQLITE_OK; - - /* If p->iStatement is greater than zero, then this Vdbe opened a - ** statement transaction that should be closed here. The only exception - ** is that an IO error may have occured, causing an emergency rollback. - ** In this case (db->nStatement==0), and there is nothing to do. - */ - if( db->nStatement && p->iStatement ){ - int i; - const int iSavepoint = p->iStatement-1; - - assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); - assert( db->nStatement>0 ); - assert( p->iStatement==(db->nStatement+db->nSavepoint) ); - - for(i=0; inDb; i++){ - int rc2 = SQLITE_OK; - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc2 = sqlite4BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc2==SQLITE_OK ){ - rc2 = sqlite4BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - db->nStatement--; - p->iStatement = 0; - - if( rc==SQLITE_OK ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc = sqlite4VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = sqlite4VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); - } - } - - /* If the statement transaction is being rolled back, also restore the - ** database handles deferred constraint counter to the value it had when - ** the statement transaction was opened. */ - if( eOp==SAVEPOINT_ROLLBACK ){ - db->nDeferredCons = p->nStmtDefCons; - } - } - return rc; + return SQLITE_OK; } /* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be @@ -2132,179 +1790,72 @@ ** Return an error code. If the commit could not complete because of ** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it ** means the close did not happen and needs to be repeated. */ int sqlite4VdbeHalt(Vdbe *p){ - int rc; /* Used to store transient return codes */ + int rc; sqlite4 *db = p->db; - /* This function contains the logic that determines if a statement or - ** transaction will be committed or rolled back as a result of the - ** execution of this virtual machine. - ** - ** If any of the following errors occur: - ** - ** SQLITE_NOMEM - ** SQLITE_IOERR - ** SQLITE_FULL - ** SQLITE_INTERRUPT - ** - ** Then the internal cache might have been left in an inconsistent - ** state. We need to rollback the statement transaction, if there is - ** one, or the complete transaction if there is no statement transaction. - */ - - if( p->db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - } + if( db->mallocFailed ) p->rc = SQLITE_NOMEM; if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag); closeAllCursors(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } checkActiveVdbeCnt(db); - /* No commit or rollback needed if the program never started */ - if( p->pc>=0 ){ - int mrc; /* Primary error code from p->rc */ - int eStatementOp = 0; - int isSpecialError; /* Set to true if a 'special' error */ - - /* Lock all btrees used by the statement */ - sqlite4VdbeEnter(p); - - /* Check for one of the special errors */ - mrc = p->rc & 0xff; - assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ - isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR - || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; - if( isSpecialError ){ - /* If the query was read-only and the error code is SQLITE_INTERRUPT, - ** no rollback is necessary. Otherwise, at least a savepoint - ** transaction must be rolled back to restore the database to a - ** consistent state. - ** - ** Even if the statement is read-only, it is important to perform - ** a statement or transaction rollback operation. If the error - ** occured while writing to the journal, sub-journal or database - ** file as part of an effort to free up cache space (see function - ** pagerStress() in pager.c), the rollback is required to restore - ** the pager to a consistent state. - */ - if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - /* We are forced to roll back the active transaction. Before doing - ** so, abort any other statements this handle currently has active. - */ - invalidateCursorsOnModifiedBtrees(db); - sqlite4RollbackAll(db); - sqlite4CloseSavepoints(db); - db->autoCommit = 1; - } - } - } - + if( p->pc<0 ){ + /* No commit or rollback needed if the program never started */ + }else{ /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK ){ sqlite4VdbeCheckFk(p, 0); } /* If the auto-commit flag is set and this is the only active writer ** VM, then we do either a commit or rollback of the current transaction. - ** - ** Note: This block also runs if one of the special errors handled - ** above has occurred. */ if( !sqlite4VtabInSync(db) && db->autoCommit && db->writeVdbeCnt==(p->readOnly==0) ){ - if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - rc = sqlite4VdbeCheckFk(p, 1); - if( rc!=SQLITE_OK ){ - if( NEVER(p->readOnly) ){ - sqlite4VdbeLeave(p); - return SQLITE_ERROR; - } - rc = SQLITE_CONSTRAINT; - }else{ - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - } - if( rc==SQLITE_BUSY && p->readOnly ){ - sqlite4VdbeLeave(p); - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - sqlite4RollbackAll(db); - }else{ - db->nDeferredCons = 0; - sqlite4CommitInternalChanges(db); - } - }else{ - sqlite4RollbackAll(db); - } - db->nStatement = 0; - }else if( eStatementOp==0 ){ - if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - eStatementOp = SAVEPOINT_RELEASE; - }else if( p->errorAction==OE_Abort ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - invalidateCursorsOnModifiedBtrees(db); - sqlite4RollbackAll(db); - sqlite4CloseSavepoints(db); - db->autoCommit = 1; - } - } - - /* If eStatementOp is non-zero, then a statement transaction needs to - ** be committed or rolled back. Call sqlite4VdbeCloseStatement() to - ** do so. If this operation returns an error, and the current statement - ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the - ** current statement error code. - */ - if( eStatementOp ){ - rc = sqlite4VdbeCloseStatement(p, eStatementOp); - if( rc ){ - if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){ - p->rc = rc; - sqlite4DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } - invalidateCursorsOnModifiedBtrees(db); - sqlite4RollbackAll(db); - sqlite4CloseSavepoints(db); - db->autoCommit = 1; - } - } - - /* If this was an INSERT, UPDATE or DELETE and no statement transaction - ** has been rolled back, update the database connection change-counter. - */ - if( p->changeCntOn ){ - if( eStatementOp!=SAVEPOINT_ROLLBACK ){ - sqlite4VdbeSetChanges(db, p->nChange); - }else{ - sqlite4VdbeSetChanges(db, 0); - } - p->nChange = 0; - } - - /* Rollback or commit any schema changes that occurred. */ - if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ - sqlite4ResetInternalSchema(db, -1); - db->flags = (db->flags | SQLITE_InternChanges); - } - - /* Release the locks */ - sqlite4VdbeLeave(p); + rc = sqlite4VdbeCheckFk(p, 1); + if( rc!=SQLITE_OK ){ + rc = SQLITE_CONSTRAINT; + }else{ + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ + rc = vdbeCommit(db, p); + } + if( rc==SQLITE_BUSY && p->readOnly ){ + /* will return the error */ + }else if( rc!=SQLITE_OK ){ + p->rc = rc; + sqlite4RollbackAll(db); + }else{ + db->nDeferredCons = 0; + sqlite4CommitInternalChanges(db); + } + }else{ + /* Not in auto-commit mode. If the statement failed, rollback + ** the effects of just this one statement */ + if( p->rc!=SQLITE_OK && p->stmtTransMask!=0 ){ + int i; + for(i=0; inDb; i++){ + if( p->stmtTransMask & ((yDbMask)1)<aDb[i].pKV; + rc = sqlite4KVStoreRollback(pKV, pKV->iTransLevel-1); + if( rc ){ + sqlite4RollbackAll(db); + break; + } + } + } + } + } } /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; @@ -2317,19 +1868,10 @@ checkActiveVdbeCnt(db); if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } - /* If the auto-commit flag is set to true, then any locks that were held - ** by connection db have now been released. Call sqlite4ConnectionUnlocked() - ** to invoke any required unlock-notify callbacks. - */ - if( db->autoCommit ){ - sqlite4ConnectionUnlocked(db); - } - - assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } /* @@ -2521,52 +2063,10 @@ p->magic = VDBE_MAGIC_DEAD; p->db = 0; sqlite4VdbeDeleteObject(db, p); } -/* -** Make sure the cursor p is ready to read or write the row to which it -** was last positioned. Return an error code if an OOM fault or I/O error -** prevents us from positioning the cursor to its correct position. -** -** If a MoveTo operation is pending on the given cursor, then do that -** MoveTo now. If no move is pending, check to see if the row has been -** deleted out from under the cursor and if it has, mark the row as -** a NULL row. -** -** If the cursor is already pointing to the correct row and that row has -** not been deleted out from under the cursor, then this routine is a no-op. -*/ -int sqlite4VdbeCursorMoveto(VdbeCursor *p){ - if( p->deferredMoveto ){ - int res, rc; -#ifdef SQLITE_TEST - extern int sqlite4_search_count; -#endif - assert( p->isTable ); - rc = sqlite4BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res); - if( rc ) return rc; - p->lastRowid = p->movetoTarget; - if( res!=0 ) return SQLITE_CORRUPT_BKPT; - p->rowidIsValid = 1; -#ifdef SQLITE_TEST - sqlite4_search_count++; -#endif - p->deferredMoveto = 0; - p->cacheStatus = CACHE_STALE; - }else if( ALWAYS(p->pCursor) ){ - int hasMoved; - int rc = sqlite4BtreeCursorHasMoved(p->pCursor, &hasMoved); - if( rc ) return rc; - if( hasMoved ){ - p->cacheStatus = CACHE_STALE; - p->nullRow = 1; - } - } - return SQLITE_OK; -} - /* ** The following functions: ** ** sqlite4VdbeSerialType() ** sqlite4VdbeSerialTypeLen() @@ -3074,128 +2574,10 @@ } return rc; } -/* -** pCur points at an index entry created using the OP_MakeRecord opcode. -** Read the rowid (the last field in the record) and store it in *rowid. -** Return SQLITE_OK if everything works, or an error code otherwise. -** -** pCur might be pointing to text obtained from a corrupt database file. -** So the content cannot be trusted. Do appropriate checks on the content. -*/ -int sqlite4VdbeIdxRowid(sqlite4 *db, BtCursor *pCur, i64 *rowid){ - i64 nCellKey = 0; - int rc; - u32 szHdr; /* Size of the header */ - u32 typeRowid; /* Serial type of the rowid */ - u32 lenRowid; /* Size of the rowid */ - Mem m, v; - - UNUSED_PARAMETER(db); - - /* Get the size of the index entry. Only indices entries of less - ** than 2GiB are support - anything large must be database corruption. - ** Any corruption is detected in sqlite4BtreeParseCellPtr(), though, so - ** this code can safely assume that nCellKey is 32-bits - */ - assert( sqlite4BtreeCursorIsValid(pCur) ); - VVA_ONLY(rc =) sqlite4BtreeKeySize(pCur, &nCellKey); - assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ - assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); - - /* Read in the complete content of the index entry */ - memset(&m, 0, sizeof(m)); - rc = sqlite4VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m); - if( rc ){ - return rc; - } - - /* The index entry must begin with a header size */ - (void)getVarint32((u8*)m.z, szHdr); - testcase( szHdr==3 ); - testcase( szHdr==m.n ); - if( unlikely(szHdr<3 || (int)szHdr>m.n) ){ - goto idx_rowid_corruption; - } - - /* The last field of the index should be an integer - the ROWID. - ** Verify that the last entry really is an integer. */ - (void)getVarint32((u8*)&m.z[szHdr-1], typeRowid); - testcase( typeRowid==1 ); - testcase( typeRowid==2 ); - testcase( typeRowid==3 ); - testcase( typeRowid==4 ); - testcase( typeRowid==5 ); - testcase( typeRowid==6 ); - testcase( typeRowid==8 ); - testcase( typeRowid==9 ); - if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ - goto idx_rowid_corruption; - } - lenRowid = sqlite4VdbeSerialTypeLen(typeRowid); - testcase( (u32)m.n==szHdr+lenRowid ); - if( unlikely((u32)m.npCursor; - Mem m; - - assert( sqlite4BtreeCursorIsValid(pCur) ); - VVA_ONLY(rc =) sqlite4BtreeKeySize(pCur, &nCellKey); - assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ - /* nCellKey will always be between 0 and 0xffffffff because of the say - ** that btreeParseCellPtr() and sqlite4GetVarint32() are implemented */ - if( nCellKey<=0 || nCellKey>0x7fffffff ){ - *res = 0; - return SQLITE_CORRUPT_BKPT; - } - memset(&m, 0, sizeof(m)); - rc = sqlite4VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); - if( rc ){ - return rc; - } - assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH ); - *res = sqlite4VdbeRecordCompare(m.n, m.z, pUnpacked); - sqlite4VdbeMemRelease(&m); - return SQLITE_OK; -} - /* ** This routine sets the value to be returned by subsequent calls to ** sqlite4_changes() on the database handle 'db'. */ void sqlite4VdbeSetChanges(sqlite4 *db, int nChange){ DELETED src/vdbeblob.c Index: src/vdbeblob.c ================================================================== --- src/vdbeblob.c +++ /dev/null @@ -1,469 +0,0 @@ -/* -** 2007 May 1 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code used to implement incremental BLOB I/O. -*/ - -#include "sqliteInt.h" -#include "vdbeInt.h" - -#ifndef SQLITE_OMIT_INCRBLOB - -/* -** Valid sqlite4_blob* handles point to Incrblob structures. -*/ -typedef struct Incrblob Incrblob; -struct Incrblob { - int flags; /* Copy of "flags" passed to sqlite4_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 */ - sqlite4_stmt *pStmt; /* Statement holding cursor open */ - sqlite4 *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 -** sqlite4_blob_read() or sqlite4_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 -** sqlite4DbFree(). -** -** If an error does occur, then the b-tree cursor is closed. All subsequent -** calls to sqlite4_blob_read(), blob_write() or blob_reopen() will -** immediately return SQLITE_ABORT. -*/ -static int blobSeekToRow(Incrblob *p, sqlite4_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 sqlite4_bind_int64() to avoid - ** triggering asserts related to mutexes. - */ - assert( v->aVar[0].flags&MEM_Int ); - v->aVar[0].u.i = iRow; - - rc = sqlite4_step(p->pStmt); - if( rc==SQLITE_ROW ){ - u32 type = v->apCsr[0]->aType[p->iCol]; - if( type<12 ){ - zErr = sqlite4MPrintf(p->db, "cannot open value of type %s", - type==0?"null": type==7?"real": "integer" - ); - rc = SQLITE_ERROR; - sqlite4_finalize(p->pStmt); - p->pStmt = 0; - }else{ - p->iOffset = v->apCsr[0]->aOffset[p->iCol]; - p->nByte = sqlite4VdbeSerialTypeLen(type); - p->pCsr = v->apCsr[0]->pCursor; - sqlite4BtreeEnterCursor(p->pCsr); - sqlite4BtreeCacheOverflow(p->pCsr); - sqlite4BtreeLeaveCursor(p->pCsr); - } - } - - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else if( p->pStmt ){ - rc = sqlite4_finalize(p->pStmt); - p->pStmt = 0; - if( rc==SQLITE_OK ){ - zErr = sqlite4MPrintf(p->db, "no such rowid: %lld", iRow); - rc = SQLITE_ERROR; - }else{ - zErr = sqlite4MPrintf(p->db, "%s", sqlite4_errmsg(p->db)); - } - } - - assert( rc!=SQLITE_OK || zErr==0 ); - assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE ); - - *pzErr = zErr; - return rc; -} - -/* -** Open a blob handle. -*/ -int sqlite4_blob_open( - sqlite4* db, /* The database connection */ - const char *zDb, /* The attached database containing the blob */ - const char *zTable, /* The table containing the blob */ - const char *zColumn, /* The column containing the blob */ - sqlite_int64 iRow, /* The row containing the glob */ - int flags, /* True -> read/write access, false -> read-only */ - sqlite4_blob **ppBlob /* Handle for accessing the blob returned here */ -){ - int nAttempt = 0; - int iCol; /* Index of zColumn in row-record */ - - /* This VDBE program seeks a btree cursor to the identified - ** db/table/row entry. The reason for using a vdbe program instead - ** of writing code to use the b-tree layer directly is that the - ** vdbe program will take advantage of the various transaction, - ** locking and error handling infrastructure built into the vdbe. - ** - ** After seeking the cursor, the vdbe executes an OP_ResultRow. - ** Code external to the Vdbe then "borrows" the b-tree cursor and - ** uses it to implement the blob_read(), blob_write() and - ** blob_bytes() functions. - ** - ** The sqlite4_blob_close() function finalizes the vdbe program, - ** which closes the b-tree cursor and (possibly) commits the - ** transaction. - */ - static const VdbeOpList openBlob[] = { - {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ - {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ - {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ - - /* One of the following two instructions is replaced by an OP_Noop. */ - {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ - {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, 10, 1}, /* 6: Seek the cursor */ - {OP_Column, 0, 0, 1}, /* 7 */ - {OP_ResultRow, 1, 0, 0}, /* 8 */ - {OP_Goto, 0, 5, 0}, /* 9 */ - {OP_Close, 0, 0, 0}, /* 10 */ - {OP_Halt, 0, 0, 0}, /* 11 */ - }; - - int rc = SQLITE_OK; - char *zErr = 0; - Table *pTab; - Parse *pParse = 0; - Incrblob *pBlob = 0; - - flags = !!flags; /* flags = (flags ? 1 : 0); */ - *ppBlob = 0; - - sqlite4_mutex_enter(db->mutex); - - pBlob = (Incrblob *)sqlite4DbMallocZero(db, sizeof(Incrblob)); - if( !pBlob ) goto blob_open_out; - pParse = sqlite4StackAllocRaw(db, sizeof(*pParse)); - if( !pParse ) goto blob_open_out; - - do { - memset(pParse, 0, sizeof(Parse)); - pParse->db = db; - sqlite4DbFree(db, zErr); - zErr = 0; - - sqlite4BtreeEnterAll(db); - pTab = sqlite4LocateTable(pParse, 0, zTable, zDb); - if( pTab && IsVirtual(pTab) ){ - pTab = 0; - sqlite4ErrorMsg(pParse, "cannot open virtual table: %s", zTable); - } -#ifndef SQLITE_OMIT_VIEW - if( pTab && pTab->pSelect ){ - pTab = 0; - sqlite4ErrorMsg(pParse, "cannot open view: %s", zTable); - } -#endif - if( !pTab ){ - if( pParse->zErrMsg ){ - sqlite4DbFree(db, zErr); - zErr = pParse->zErrMsg; - pParse->zErrMsg = 0; - } - rc = SQLITE_ERROR; - sqlite4BtreeLeaveAll(db); - goto blob_open_out; - } - - /* Now search pTab for the exact column. */ - for(iCol=0; iColnCol; iCol++) { - if( sqlite4StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ - break; - } - } - if( iCol==pTab->nCol ){ - sqlite4DbFree(db, zErr); - zErr = sqlite4MPrintf(db, "no such column: \"%s\"", zColumn); - rc = SQLITE_ERROR; - sqlite4BtreeLeaveAll(db); - goto blob_open_out; - } - - /* If the value is being opened for writing, check that the - ** column is not indexed, and that it is not part of a foreign key. - ** It is against the rules to open a column to which either of these - ** descriptions applies for writing. */ - if( flags ){ - const char *zFault = 0; - Index *pIdx; -#ifndef SQLITE_OMIT_FOREIGN_KEY - if( db->flags&SQLITE_ForeignKeys ){ - /* Check that the column is not part of an FK child key definition. It - ** is not necessary to check if it is part of a parent key, as parent - ** key columns must be indexed. The check below will pick up this - ** case. */ - FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ - int j; - for(j=0; jnCol; j++){ - if( pFKey->aCol[j].iFrom==iCol ){ - zFault = "foreign key"; - } - } - } - } -#endif - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int j; - for(j=0; jnColumn; j++){ - if( pIdx->aiColumn[j]==iCol ){ - zFault = "indexed"; - } - } - } - if( zFault ){ - sqlite4DbFree(db, zErr); - zErr = sqlite4MPrintf(db, "cannot open %s column for writing", zFault); - rc = SQLITE_ERROR; - sqlite4BtreeLeaveAll(db); - goto blob_open_out; - } - } - - pBlob->pStmt = (sqlite4_stmt *)sqlite4VdbeCreate(db); - assert( pBlob->pStmt || db->mallocFailed ); - if( pBlob->pStmt ){ - Vdbe *v = (Vdbe *)pBlob->pStmt; - int iDb = sqlite4SchemaToIndex(db, pTab->pSchema); - - sqlite4VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); - - - /* Configure the OP_Transaction */ - sqlite4VdbeChangeP1(v, 0, iDb); - sqlite4VdbeChangeP2(v, 0, flags); - - /* Configure the OP_VerifyCookie */ - sqlite4VdbeChangeP1(v, 1, iDb); - sqlite4VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); - sqlite4VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); - - /* Make sure a mutex is held on the table to be accessed */ - sqlite4VdbeUsesBtree(v, iDb); - - /* Configure the OP_TableLock instruction */ -#ifdef SQLITE_OMIT_SHARED_CACHE - sqlite4VdbeChangeToNoop(v, 2); -#else - sqlite4VdbeChangeP1(v, 2, iDb); - sqlite4VdbeChangeP2(v, 2, pTab->tnum); - sqlite4VdbeChangeP3(v, 2, flags); - sqlite4VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); -#endif - - /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. */ - sqlite4VdbeChangeToNoop(v, 4 - flags); - sqlite4VdbeChangeP2(v, 3 + flags, pTab->tnum); - sqlite4VdbeChangeP3(v, 3 + flags, iDb); - - /* Configure the number of columns. Configure the cursor to - ** think that the table has one more column than it really - ** does. An OP_Column to retrieve this imaginary column will - ** always return an SQL NULL. This is useful because it means - ** we can invoke OP_Column to fill in the vdbe cursors type - ** and offset cache without causing any IO. - */ - sqlite4VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite4VdbeChangeP2(v, 7, pTab->nCol); - if( !db->mallocFailed ){ - pParse->nVar = 1; - pParse->nMem = 1; - pParse->nTab = 1; - sqlite4VdbeMakeReady(v, pParse); - } - } - - pBlob->flags = flags; - pBlob->iCol = iCol; - pBlob->db = db; - sqlite4BtreeLeaveAll(db); - if( db->mallocFailed ){ - goto blob_open_out; - } - sqlite4_bind_int64(pBlob->pStmt, 1, iRow); - rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA ); - -blob_open_out: - if( rc==SQLITE_OK && db->mallocFailed==0 ){ - *ppBlob = (sqlite4_blob *)pBlob; - }else{ - if( pBlob && pBlob->pStmt ) sqlite4VdbeFinalize((Vdbe *)pBlob->pStmt); - sqlite4DbFree(db, pBlob); - } - sqlite4Error(db, rc, (zErr ? "%s" : 0), zErr); - sqlite4DbFree(db, zErr); - sqlite4StackFree(db, pParse); - rc = sqlite4ApiExit(db, rc); - sqlite4_mutex_leave(db->mutex); - return rc; -} - -/* -** Close a blob handle that was previously created using -** sqlite4_blob_open(). -*/ -int sqlite4_blob_close(sqlite4_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - int rc; - sqlite4 *db; - - if( p ){ - db = p->db; - sqlite4_mutex_enter(db->mutex); - rc = sqlite4_finalize(p->pStmt); - sqlite4DbFree(db, p); - sqlite4_mutex_leave(db->mutex); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Perform a read or write operation on a blob -*/ -static int blobReadWrite( - sqlite4_blob *pBlob, - void *z, - int n, - int iOffset, - int (*xCall)(BtCursor*, u32, u32, void*) -){ - int rc; - Incrblob *p = (Incrblob *)pBlob; - Vdbe *v; - sqlite4 *db; - - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlite4_mutex_enter(db->mutex); - v = (Vdbe*)p->pStmt; - - if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ - /* Request is out of range. Return a transient error. */ - rc = SQLITE_ERROR; - sqlite4Error(db, SQLITE_ERROR, 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. - */ - rc = SQLITE_ABORT; - }else{ - /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is - ** returned, clean-up the statement handle. - */ - assert( db == v->db ); - sqlite4BtreeEnterCursor(p->pCsr); - rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); - sqlite4BtreeLeaveCursor(p->pCsr); - if( rc==SQLITE_ABORT ){ - sqlite4VdbeFinalize(v); - p->pStmt = 0; - }else{ - db->errCode = rc; - v->rc = rc; - } - } - rc = sqlite4ApiExit(db, rc); - sqlite4_mutex_leave(db->mutex); - return rc; -} - -/* -** Read data from a blob handle. -*/ -int sqlite4_blob_read(sqlite4_blob *pBlob, void *z, int n, int iOffset){ - return blobReadWrite(pBlob, z, n, iOffset, sqlite4BtreeData); -} - -/* -** Write data to a blob handle. -*/ -int sqlite4_blob_write(sqlite4_blob *pBlob, const void *z, int n, int iOffset){ - return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite4BtreePutData); -} - -/* -** Query a blob handle for the size of the data. -** -** The Incrblob.nByte field is fixed for the lifetime of the Incrblob -** so no mutex is required for access. -*/ -int sqlite4_blob_bytes(sqlite4_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - 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 sqlite4_blob_xxx() functions (except blob_close()) -** immediately return SQLITE_ABORT. -*/ -int sqlite4_blob_reopen(sqlite4_blob *pBlob, sqlite4_int64 iRow){ - int rc; - Incrblob *p = (Incrblob *)pBlob; - sqlite4 *db; - - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlite4_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 ){ - sqlite4Error(db, rc, (zErr ? "%s" : 0), zErr); - sqlite4DbFree(db, zErr); - } - assert( rc!=SQLITE_SCHEMA ); - } - - rc = sqlite4ApiExit(db, rc); - assert( rc==SQLITE_OK || p->pStmt==0 ); - sqlite4_mutex_leave(db->mutex); - return rc; -} - -#endif /* #ifndef SQLITE_OMIT_INCRBLOB */ Index: src/vdbecodec.c ================================================================== --- src/vdbecodec.c +++ src/vdbecodec.c @@ -75,22 +75,31 @@ sqlite4_uint64 type; /* Datatype */ sqlite4_uint64 subtype; /* Subtype for a typed blob */ int cclass; /* class of content */ int n; /* Offset into the header */ int i; /* Loop counter */ + int sz; /* Size of a varint */ int endHdr; /* First byte past header */ sqlite4VdbeMemSetNull(pOut); assert( iVal<=p->mxCol ); - n = sqlite4GetVarint64(p->a, &ofst); + n = sqlite4GetVarint64(p->a, p->n, &ofst); + if( n==0 ) return SQLITE_CORRUPT; + ofst += n; endHdr = ofst; if( endHdr>p->n ) return SQLITE_CORRUPT; for(i=0; i<=iVal && na+n, &type); + sz = sqlite4GetVarint64(p->a+n, p->n-n, &type); + if( sz==0 ) return SQLITE_CORRUPT; + n += sz; if( type>=22 ){ cclass = (type-22)%3; - if( cclass==2 ) n += sqlite4GetVarint64(p->a+n, &subtype); + if( cclass==2 ){ + sz = sqlite4GetVarint64(p->a+n, p->n-n, &subtype); + if( sz==0 ) return SQLITE_CORRUPT; + n += sz; + } size = (type-22)/3; }else if( type<=2 ){ size = 0; }else if( type<=10 ){ size = type - 2; @@ -109,13 +118,13 @@ sqlite4VdbeMemSetInt64(pOut, v); }else if( type<=21 ){ sqlite4_uint64 x; int e; double r; - n = sqlite4GetVarint64(p->a+ofst, &x); + n = sqlite4GetVarint64(p->a+ofst, p->n-ofst, &x); e = (int)x; - n += sqlite4GetVarint64(p->a+ofst+n, &x); + n += sqlite4GetVarint64(p->a+ofst+n, p->n-(ofst+n), &x); if( n!=size ) return SQLITE_CORRUPT; r = (double)x; if( e&1 ) r = -r; if( e&2 ){ e = -(e>>2); @@ -140,11 +149,17 @@ } }else{ sqlite4VdbeMemSetStr(pOut, (char*)(p->a+ofst), size, 0, SQLITE_TRANSIENT); } } - if( i0 ); + do{ + aDigits[i++] = m%100; m /= 100; + }while( m ); + p->nOut += sqlite4PutVarint64(p->aOut+p->nOut, i); + while( i ) p->aOut[p->nOut++] = aDigits[--i]*2 + 1; + p->aOut[p->nOut-1] &= 0xfe; +} + +/* +** Encode a single integer using the key encoding. The caller must +** ensure that sufficient space exits in a[] (at least 12 bytes). +** The return value is the number of bytes of a[] used. +*/ +int sqlite4VdbeEncodeIntKey(u8 *a, sqlite4_int64 v){ int i; - unsigned char aDigits[30]; - aDigits[0] = 0; - aDigits[1] = 0; - for(i=2; m; i++){ aDigits[i] = (m%10)+1; m /= 10; } - p->nOut += sqlite4PutVarint64(p->aOut+p->nOut, (i-2)); - i--; - while( i>0 ){ - p->aOut[p->nOut++] = aDigits[i]*16 + aDigits[i-1]; - i -= 2; - } + KeyEncoder s; + s.aOut = a; + s.nOut = 1; + if( v<0 ){ + a[0] = 0x08; + encodeIntKey((sqlite4_uint64)-v, &s); + for(i=1; i0.0 && r<1.0 ); - while( r<1e-8 ){ r *= 1e8; e=8; } - while( r<1.0 ){ r *= 10.0; e++; } + while( r<1e-8 ){ r *= 1e8; e+=4; } + while( r<1.0 ){ r *= 100.0; e++; } n = sqlite4PutVarint64(p->aOut+p->nOut, e); for(i=0; iaOut[i+p->nOut] ^= 0xff; p->nOut += n; for(i=0; i<18 && r!=0.0; i++){ int d = r; - aDigits[i] = 1+d; + p->aOut[p->nOut++] = 2*d + 1; r -= d; - r *= 10.0; + r *= 100.0; } - aDigits[i] = 0; - aDigits[i+1] = 0; - for(j=0; j<=i; j += 2){ - p->aOut[p->nOut++] = aDigits[j]*16 + aDigits[j+1]; - } + p->aOut[p->nOut-1] &= 0xfe; } /* ** Encode the large positive floating point number r using the key ** encoding. The caller guarantees that r will be finite and greater than @@ -403,30 +432,25 @@ ** be non-negative here. The mantissa is stored two-digits per byte ** as described for the integer encoding above. */ static void encodeLargeFloatKey(double r, KeyEncoder *p){ int e = 0; - int i, j, n; - unsigned char aDigits[30]; + int i, n; assert( r>=1.0 ); - while( r>=1e32 && e<=350 ){ r *= 1e-32; e+=32; } - while( r>=1e8 && e<=350 ){ r *= 1e-8; e+=8; } - while( r>=10.0 && e<=350 ){ r *= 0.1; e++; } + while( r>=1e32 && e<=350 ){ r *= 1e-32; e+=16; } + while( r>=1e8 && e<=350 ){ r *= 1e-8; e+=4; } + while( r>=100.0 && e<=350 ){ r *= 0.01; e++; } while( r<1.0 ){ r *= 10.0; e--; } n = sqlite4PutVarint64(p->aOut+p->nOut, e); p->nOut += n; for(i=0; i<18 && r!=0.0; i++){ int d = r; - aDigits[i] = 1+d; + p->aOut[p->nOut++] = 2*d + 1; r -= d; - r *= 10.0; + r *= 100.0; } - aDigits[i] = 0; - aDigits[i+1] = 0; - for(j=0; j<=i; j += 2){ - p->aOut[p->nOut++] = aDigits[j]*16 + aDigits[j+1]; - } + p->aOut[p->nOut-1] &= 0xfe; } /* ** Encode a single column of the key @@ -546,35 +570,95 @@ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence information */ u8 **paOut, /* Write the resulting key here */ - int *pnOut /* Number of bytes in the key */ + int *pnOut, /* Number of bytes in the key */ + int *pnShort /* Number of bytes without the primary key */ ){ int i; int rc = SQLITE_OK; KeyEncoder x; - u8 *so = pKeyInfo->aSortOrder; + u8 *so; + int iShort; + int nField; + CollSeq **aColl; + CollSeq *xColl; + static const CollSeq defaultColl; x.db = db; x.aOut = 0; x.nOut = 0; x.nAlloc = 0; *paOut = 0; *pnOut = 0; if( enlargeEncoderAllocation(&x, (nIn+1)*10) ) return SQLITE_NOMEM; x.nOut = sqlite4PutVarint64(x.aOut, iTabno); - for(i=0; inField && rc==SQLITE_OK; i++){ - rc = encodeOneKeyValue(&x, aIn+i, so ? so[i] : SQLITE_SO_ASC, - pKeyInfo->aColl[i]); + if( pKeyInfo ){ + nField = pKeyInfo->nField; + iShort = nField - pKeyInfo->nPK; + aColl = pKeyInfo->aColl; + so = pKeyInfo->aSortOrder; + }else{ + nField = 1; + iShort = 0; + xColl = &defaultColl; + aColl = &xColl; + so = 0; } - for(; iaColl[0]); + for(i=0; isizeof(aBuf) ) nKey = sizeof(aBuf); + if( aKey[0]==0x08 ){ + isNeg = 1; + memcpy(aBuf, aKey, nKey); + aKey = aBuf; + for(i=1; i10 || m==0 ) return 0; + e = m; + if( n==0 || n+1>=nKey ) return 0; + m = 0; + i = n+1; + do{ + m = m*100 + aKey[i]/2; + e--; + }while( aKey[i++] & 1 ); + if( isNeg ){ + *pVal = -m; + }else{ + *pVal = m; + } + return m==0 ? 0 : i; +} ADDED src/vdbecursor.c Index: src/vdbecursor.c ================================================================== --- /dev/null +++ src/vdbecursor.c @@ -0,0 +1,102 @@ +/* +** 2012 February 16 +** +** 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 methods for the VdbeCursor object. +** +** A VdbeCursor is an abstraction of the KVCursor that includes knowledge +** about different "tables" in the key space. A VdbeCursor is only active +** over a particular table. Thus, for example, sqlite4VdbeNext() will +** return SQLITE_NOTFOUND when advancing off the end of a table into the +** next table whereas the lower-level sqlite4KVCursorNext() routine will +** not return SQLITE_NOTFOUND until it is advanced off the end of the very +** last table in the database. +*/ +#include "sqliteInt.h" +#include "vdbeInt.h" + + +/* +** Move a VDBE cursor to the first or to the last element of its table. The +** first element is sought if iEnd==+1 and the last element if iEnd==-1. +** +** Return SQLITE_OK on success. Return SQLITE_NOTFOUND if the table is empty. +* Other error codes are also possible for various kinds of errors. +*/ +int sqlite4VdbeSeekEnd(VdbeCursor *pC, int iEnd){ + KVCursor *pCur = pC->pKVCur; + const KVByteArray *aKey; + KVSize nKey; + KVSize nProbe; + int rc; + KVByteArray aProbe[16]; + + assert( iEnd==(+1) || iEnd==(-1) ); + nProbe = sqlite4PutVarint64(aProbe, pC->iRoot); + aProbe[nProbe++] = 10 - iEnd*6; + rc = sqlite4KVCursorSeek(pCur, aProbe, nProbe, iEnd); + if( rc==SQLITE_OK ){ + return SQLITE_CORRUPT; + } + if( rc==SQLITE_INEXACT ){ + rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); + if( rc==SQLITE_OK && (nKeypKVCur; + const KVByteArray *aKey; + KVSize nKey; + int rc; + sqlite4_uint64 iTabno; + + rc = sqlite4KVCursorNext(pCur); + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + iTabno = 0; + sqlite4GetVarint64(aKey, nKey, &iTabno); + if( iTabno!=pC->iRoot ) rc = SQLITE_NOTFOUND; + } + } + return rc; +} + +/* +** Move a VDBE cursor to the previous element in its table. +** Return SQLITE_NOTFOUND if the seek falls of the end of the table. +*/ +int sqlite4VdbePrevious(VdbeCursor *pC){ + KVCursor *pCur = pC->pKVCur; + const KVByteArray *aKey; + KVSize nKey; + int rc; + sqlite4_uint64 iTabno; + + rc = sqlite4KVCursorPrev(pCur); + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + iTabno = 0; + sqlite4GetVarint64(aKey, nKey, &iTabno); + if( iTabno!=pC->iRoot ) rc = SQLITE_NOTFOUND; + } + } + return rc; +} Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -133,39 +133,10 @@ } return SQLITE_OK; } -/* -** If the given Mem* has a zero-filled tail, turn it into an ordinary -** blob stored in dynamically allocated space. -*/ -#ifndef SQLITE_OMIT_INCRBLOB -int sqlite4VdbeMemExpandBlob(Mem *pMem){ - if( pMem->flags & MEM_Zero ){ - int nByte; - assert( pMem->flags&MEM_Blob ); - assert( (pMem->flags&MEM_RowSet)==0 ); - assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); - - /* Set nByte to the number of bytes required to store the expanded blob. */ - nByte = pMem->n + pMem->u.nZero; - if( nByte<=0 ){ - nByte = 1; - } - if( sqlite4VdbeMemGrow(pMem, nByte, 1) ){ - return SQLITE_NOMEM; - } - - memset(&pMem->z[pMem->n], 0, pMem->u.nZero); - pMem->n += pMem->u.nZero; - pMem->flags &= ~(MEM_Zero|MEM_Term); - } - return SQLITE_OK; -} -#endif - /* ** Make sure the given Mem is \u0000 terminated. */ int sqlite4VdbeMemNulTerminate(Mem *pMem){ @@ -498,18 +469,10 @@ pMem->type = SQLITE_BLOB; pMem->n = 0; if( n<0 ) n = 0; pMem->u.nZero = n; pMem->enc = SQLITE_UTF8; - -#ifdef SQLITE_OMIT_INCRBLOB - sqlite4VdbeMemGrow(pMem, n, 0); - if( pMem->z ){ - pMem->n = n; - memset(pMem->z, 0, n); - } -#endif } /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. @@ -869,70 +832,10 @@ /* Both values must be blobs. Compare using memcmp(). */ rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); if( rc==0 ){ rc = pMem1->n - pMem2->n; } - return rc; -} - -/* -** Move data out of a btree key or data field and into a Mem structure. -** The data or key is taken from the entry that pCur is currently pointing -** to. offset and amt determine what portion of the data or key to retrieve. -** key is true to get the key or false to get data. The result is written -** into the pMem element. -** -** The pMem structure is assumed to be uninitialized. Any prior content -** is overwritten without being freed. -** -** If this routine fails for any reason (malloc returns NULL or unable -** to read from the disk) then the pMem is left in an inconsistent state. -*/ -int sqlite4VdbeMemFromBtree( - BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - int offset, /* Offset from the start of data to return bytes from. */ - int amt, /* Number of bytes to return. */ - int key, /* If true, retrieve from the btree key, not data. */ - Mem *pMem /* OUT: Return data in this Mem structure. */ -){ - char *zData; /* Data from the btree layer */ - int available = 0; /* Number of bytes available on the local btree page */ - int rc = SQLITE_OK; /* Return code */ - - assert( sqlite4BtreeCursorIsValid(pCur) ); - - /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() - ** that both the BtShared and database handle mutexes are held. */ - assert( (pMem->flags & MEM_RowSet)==0 ); - if( key ){ - zData = (char *)sqlite4BtreeKeyFetch(pCur, &available); - }else{ - zData = (char *)sqlite4BtreeDataFetch(pCur, &available); - } - assert( zData!=0 ); - - if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){ - sqlite4VdbeMemRelease(pMem); - pMem->z = &zData[offset]; - pMem->flags = MEM_Blob|MEM_Ephem; - }else if( SQLITE_OK==(rc = sqlite4VdbeMemGrow(pMem, amt+2, 0)) ){ - pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term; - pMem->enc = 0; - pMem->type = SQLITE_BLOB; - if( key ){ - rc = sqlite4BtreeKey(pCur, offset, amt, pMem->z); - }else{ - rc = sqlite4BtreeData(pCur, offset, amt, pMem->z); - } - pMem->z[amt] = 0; - pMem->z[amt+1] = 0; - if( rc!=SQLITE_OK ){ - sqlite4VdbeMemRelease(pMem); - } - } - pMem->n = amt; - return rc; } /* This function is only available internally, it is not part of the ** external API. It works in a similar way to sqlite4_value_text(), Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -382,11 +382,10 @@ /* ** Initialize the temporary index cursor just opened as a sorter cursor. */ int sqlite4VdbeSorterInit(sqlite4 *db, VdbeCursor *pCsr){ - int pgsz; /* Page size of main database */ int mxCache; /* Cache size */ VdbeSorter *pSorter; /* The new sorter */ char *d; /* Dummy */ assert( pCsr->pKeyInfo && pCsr->pBt==0 ); @@ -398,15 +397,11 @@ pSorter->pUnpacked = sqlite4VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d); if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM; assert( pSorter->pUnpacked==(UnpackedRecord *)d ); if( !sqlite4TempInMemory(db) ){ - pgsz = sqlite4BtreeGetPageSize(db->aDb[0].pBt); - pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz; - mxCache = db->aDb[0].pSchema->cache_size; - if( mxCachemxPmaSize = mxCache * pgsz; + pSorter->mnPmaSize = 100000; } return SQLITE_OK; } Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -151,18 +151,10 @@ static VTable *vtabDisconnectAll(sqlite4 *db, Table *p){ VTable *pRet = 0; VTable *pVTable = p->pVTable; p->pVTable = 0; - /* Assert that the mutex (if any) associated with the BtShared database - ** that contains table p is held by the caller. See header comments - ** above function sqlite4VtabUnlockList() for an explanation of why - ** this makes it safe to access the sqlite4.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. - */ - assert( db==0 || sqlite4SchemaMutexHeld(db, 0, p->pSchema) ); - while( pVTable ){ sqlite4 *db2 = pVTable->db; VTable *pNext = pVTable->pNext; assert( db2 ); if( db2==db ){ @@ -203,11 +195,10 @@ */ void sqlite4VtabUnlockList(sqlite4 *db){ VTable *p = db->pDisconnect; db->pDisconnect = 0; - assert( sqlite4BtreeHoldsAllMutexes(db) ); assert( sqlite4_mutex_held(db->mutex) ); if( p ){ sqlite4ExpirePreparedStatements(db); do { @@ -396,11 +387,10 @@ else { Table *pOld; Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; int nName = sqlite4Strlen30(zName); - assert( sqlite4SchemaMutexHeld(db, 0, pSchema) ); pOld = sqlite4HashInsert(&pSchema->tblHash, zName, nName, pTab); if( pOld ){ db->mallocFailed = 1; assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */ return; Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -1620,11 +1620,11 @@ nTerm = pOrderBy->nExpr; assert( nTerm>0 ); /* Argument pIdx must either point to a 'real' named index structure, - ** or an index structure allocated on the stack by bestBtreeIndex() to + ** or an index structure allocated on the stack by bestKVIndex() to ** represent the rowid index that is part of every table. */ assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); /* Match terms of the ORDER BY clause against columns of ** the index. @@ -2412,11 +2412,11 @@ } } /* If there is an ORDER BY clause, and the selected virtual table index ** does not satisfy it, increase the cost of the scan accordingly. This - ** matches the processing for non-virtual tables in bestBtreeIndex(). + ** matches the processing for non-virtual tables in bestKVIndex(). */ rCost = pIdxInfo->estimatedCost; if( pOrderBy && pIdxInfo->orderByConsumed==0 ){ rCost += estLog(rCost)*rCost; } @@ -2869,11 +2869,11 @@ ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table ** in the SELECT statement, then no indexes are considered. However, the ** selected plan may still take advantage of the built-in rowid primary key ** index. */ -static void bestBtreeIndex( +static void bestKVIndex( 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 not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ @@ -3372,11 +3372,11 @@ } sqlite4DbFree(pParse->db, p); }else #endif { - bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, 0, pCost); + bestKVIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, 0, pCost); } } /* ** Disable a term in the WHERE clause. Except, do not disable the term @@ -4536,11 +4536,11 @@ ** appear in the FROM clause if a different order is better able to make ** use of indices. Note also that when the IN operator appears in ** the WHERE clause, it might result in additional nested loops for ** scanning through all values on the right-hand side of the IN. ** -** There are Btree cursors associated with each table. t1 uses cursor +** There are cursors associated with each table. t1 uses cursor ** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. ** And so forth. This routine generates code to open those VDBE cursors ** and sqlite4WhereEnd() generates the code to close them. ** ** The code that sqlite4WhereBegin() generates leaves the cursors named @@ -4846,11 +4846,11 @@ bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else #endif { - bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, + bestKVIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, pDist, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that DELETED test/backup.test Index: test/backup.test ================================================================== --- test/backup.test +++ /dev/null @@ -1,972 +0,0 @@ -# 2009 January 30 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this file is testing the sqlite4_backup_XXX API. -# -# $Id: backup.test,v 1.11 2009/06/05 17:09:12 drh Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -do_not_use_codec - -#--------------------------------------------------------------------- -# Test organization: -# -# backup-1.*: Warm-body tests. -# -# backup-2.*: Test backup under various conditions. To and from in-memory -# databases. To and from empty/populated databases. etc. -# -# backup-3.*: Verify that the locking-page (pending byte page) is handled. -# -# backup-4.*: Test various error conditions. -# -# backup-5.*: Test the source database being modified during a backup. -# -# backup-6.*: Test the backup_remaining() and backup_pagecount() APIs. -# -# backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors. -# -# backup-8.*: Test multiple simultaneous backup operations. -# -# backup-9.*: Test that passing a negative argument to backup_step() is -# interpreted as "copy the whole file". -# -# backup-10.*: Test writing the source database mid backup. -# - -proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" } -proc test_contents {name db1 file1 db2 file2} { - $db2 eval {select * from sqlite_master} - $db1 eval {select * from sqlite_master} - set checksum [data_checksum $db2 $file2] - uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum] -} - -do_test backup-1.1 { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - COMMIT; - } -} {} - -# Sanity check to verify that the [test_contents] proc works. -# -test_contents backup-1.2 db main db main - -# Check that it is possible to create and finish backup operations. -# -do_test backup-1.3.1 { - delete_file test2.db - sqlite4 db2 test2.db - sqlite4_backup B db2 main db main -} {B} -do_test backup-1.3.2 { - B finish -} {SQLITE_OK} -do_test backup-1.3.3 { - info commands B -} {} - -# Simplest backup operation. Backup test.db to test2.db. test2.db is -# initially empty. test.db uses the default page size. -# -do_test backup-1.4.1 { - sqlite4_backup B db2 main db main -} {B} -do_test backup-1.4.2 { - B step 200 -} {SQLITE_DONE} -do_test backup-1.4.3 { - B finish -} {SQLITE_OK} -do_test backup-1.4.4 { - info commands B -} {} -test_contents backup-1.4.5 db2 main db main -db close -db2 close -# -# End of backup-1.* tests. -#--------------------------------------------------------------------- - - -#--------------------------------------------------------------------- -# The following tests, backup-2.*, are based on the following procedure: -# -# 1) Populate the source database. -# 2) Populate the destination database. -# 3) Run the backup to completion. (backup-2.*.1) -# 4) Integrity check the destination db. (backup-2.*.2) -# 5) Check that the contents of the destination db is the same as that -# of the source db. (backup-2.*.3) -# -# The test is run with all possible combinations of the following -# input parameters, except that if the destination is an in-memory -# database, the only page size tested is 1024 bytes (the same as the -# source page-size). -# -# * Source database is an in-memory database, OR -# * Source database is a file-backed database. -# -# * Target database is an in-memory database, OR -# * Target database is a file-backed database. -# -# * Destination database is a main file, OR -# * Destination database is an attached file, OR -# * Destination database is a temp database. -# -# * Target database is empty (zero bytes), OR -# * Target database is larger than the source, OR -# * Target database is smaller than the source. -# -# * Target database page-size is the same as the source, OR -# * Target database page-size is larger than the source, OR -# * Target database page-size is smaller than the source. -# -# * Each call to step copies a single page, OR -# * A single call to step copies the entire source database. -# -set iTest 1 -foreach zSrcFile {test.db :memory:} { -foreach zDestFile {test2.db :memory:} { -foreach zOpenScript [list { - sqlite4 db $zSrcFile - sqlite4 db2 $zSrcFile - db2 eval "ATTACH '$zDestFile' AS bak" - set db_dest db2 - set file_dest bak -} { - sqlite4 db $zSrcFile - sqlite4 db2 $zDestFile - set db_dest db2 - set file_dest main -} { - sqlite4 db $zSrcFile - sqlite4 db2 $zDestFile - set db_dest db2 - set file_dest temp -}] { -foreach rows_dest {0 3 10} { -foreach pgsz_dest {512 1024 2048} { -foreach nPagePerStep {1 200} { - - # Open the databases. - catch { delete_file test.db } - catch { delete_file test2.db } - eval $zOpenScript - - # Set to true if copying to an in-memory destination. Copying to an - # in-memory destination is only possible if the initial destination - # page size is the same as the source page size (in this case 1024 bytes). - # - set isMemDest [expr { - $zDestFile eq ":memory:" || $file_dest eq "temp" && $TEMP_STORE>=2 - }] - - if { $isMemDest==0 || $pgsz_dest == 1024 } { - if 0 { - puts -nonewline "Test $iTest: src=$zSrcFile dest=$zDestFile" - puts -nonewline " (as $db_dest.$file_dest)" - puts -nonewline " rows_dest=$rows_dest pgsz_dest=$pgsz_dest" - puts "" - } - - # Set up the content of the source database. - execsql { - PRAGMA page_size = 1024; - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - COMMIT; - } - - - - # Set up the content of the target database. - execsql "PRAGMA ${file_dest}.page_size = ${pgsz_dest}" $db_dest - if {$rows_dest != 0} { - execsql " - BEGIN; - CREATE TABLE ${file_dest}.t1(a, b); - CREATE INDEX ${file_dest}.i1 ON t1(a, b); - " $db_dest - for {set ii 0} {$ii < $rows_dest} {incr ii} { - execsql " - INSERT INTO ${file_dest}.t1 VALUES(1, randstr(1000,1000)) - " $db_dest - } - } - - # Backup the source database. - do_test backup-2.$iTest.1 { - sqlite4_backup B $db_dest $file_dest db main - while {[B step $nPagePerStep]=="SQLITE_OK"} {} - B finish - } {SQLITE_OK} - - # Run integrity check on the backup. - do_test backup-2.$iTest.2 { - execsql "PRAGMA ${file_dest}.integrity_check" $db_dest - } {ok} - - test_contents backup-2.$iTest.3 db main $db_dest $file_dest - - } - - db close - catch {db2 close} - incr iTest - -} } } } } } -# -# End of backup-2.* tests. -#--------------------------------------------------------------------- - -#--------------------------------------------------------------------- -# These tests, backup-3.*, ensure that nothing goes wrong if either -# the source or destination database are large enough to include the -# the locking-page (the page that contains the range of bytes that -# the locks are applied to). These tests assume that the pending -# byte is at offset 0x00010000 (64KB offset), as set by tester.tcl, -# not at the 1GB offset as it usually is. -# -# The test procedure is as follows (same procedure as used for -# the backup-2.* tests): -# -# 1) Populate the source database. -# 2) Populate the destination database. -# 3) Run the backup to completion. (backup-3.*.1) -# 4) Integrity check the destination db. (backup-3.*.2) -# 5) Check that the contents of the destination db is the same as that -# of the source db. (backup-3.*.3) -# -# The test procedure is run with the following parameters varied: -# -# * Source database includes pending-byte page. -# * Source database does not include pending-byte page. -# -# * Target database includes pending-byte page. -# * Target database does not include pending-byte page. -# -# * Target database page-size is the same as the source, OR -# * Target database page-size is larger than the source, OR -# * Target database page-size is smaller than the source. -# -set iTest 1 -foreach nSrcPg {10 64 65 66 100} { -foreach nDestRow {10 100} { -foreach nDestPgsz {512 1024 2048 4096} { - - catch { delete_file test.db } - catch { delete_file test2.db } - sqlite4 db test.db - sqlite4 db2 test2.db - - # Set up the content of the two databases. - # - execsql { PRAGMA page_size = 1024 } - execsql "PRAGMA page_size = $nDestPgsz" db2 - foreach db {db db2} { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - COMMIT; - } $db - } - while {[file size test.db]/1024 < $nSrcPg} { - execsql { INSERT INTO t1 VALUES($ii, randstr(200,200)) } - } - - for {set ii 0} {$ii < $nDestRow} {incr ii} { - execsql { INSERT INTO t1 VALUES($ii, randstr(1000,1000)) } db2 - } - - # Backup the source database. - do_test backup-3.$iTest.1 { - sqlite4_backup B db main db2 main - while {[B step 10]=="SQLITE_OK"} {} - B finish - } {SQLITE_OK} - - # Run integrity check on the backup. - do_test backup-3.$iTest.2 { - execsql "PRAGMA integrity_check" db2 - } {ok} - - test_contents backup-3.$iTest.3 db main db2 main - - db close - db2 close - incr iTest -} -} -} - -#-------------------------------------------------------------------- -do_test backup-3.$iTest.1 { - catch { forcedelete test.db } - catch { forcedelete test2.db } - sqlite4 db test.db - set iTab 1 - - db eval { PRAGMA page_size = 512 } - while {[file size test.db] <= $::sqlite_pending_byte} { - db eval "CREATE TABLE t${iTab}(a, b, c)" - incr iTab - } - - sqlite4 db2 test2.db - db2 eval { PRAGMA page_size = 4096 } - while {[file size test2.db] < $::sqlite_pending_byte} { - db2 eval "CREATE TABLE t${iTab}(a, b, c)" - incr iTab - } - - sqlite4_backup B db2 main db main - B step -1 -} {SQLITE_DONE} - -do_test backup-3.$iTest.2 { - B finish -} {SQLITE_OK} - -# -# End of backup-3.* tests. -#--------------------------------------------------------------------- - - -#--------------------------------------------------------------------- -# The following tests, backup-4.*, test various error conditions: -# -# backup-4.1.*: Test invalid database names. -# -# backup-4.2.*: Test that the source database cannot be detached while -# a backup is in progress. -# -# backup-4.3.*: Test that the source database handle cannot be closed -# while a backup is in progress. -# -# backup-4.4.*: Test an attempt to specify the same handle for the -# source and destination databases. -# -# backup-4.5.*: Test that an in-memory destination with a different -# page-size to the source database is an error. -# -sqlite4 db test.db -sqlite4 db2 test2.db - -do_test backup-4.1.1 { - catch { sqlite4_backup B db aux db2 main } -} {1} -do_test backup-4.1.2 { - sqlite4_errmsg db -} {unknown database aux} -do_test backup-4.1.3 { - catch { sqlite4_backup B db main db2 aux } -} {1} -do_test backup-4.1.4 { - sqlite4_errmsg db -} {unknown database aux} - -do_test backup-4.2.1 { - catch { forcedelete test3.db } - catch { forcedelete test4.db } - execsql { - ATTACH 'test3.db' AS aux1; - CREATE TABLE aux1.t1(a, b); - } - execsql { - ATTACH 'test4.db' AS aux2; - CREATE TABLE aux2.t2(a, b); - } db2 - sqlite4_backup B db aux1 db2 aux2 -} {B} -do_test backup-4.2.2 { - catchsql { DETACH aux2 } db2 -} {1 {database aux2 is locked}} -do_test backup-4.2.3 { - B step 50 -} {SQLITE_DONE} -do_test backup-4.2.4 { - B finish -} {SQLITE_OK} - -do_test backup-4.3.1 { - sqlite4_backup B db aux1 db2 aux2 -} {B} -do_test backup-4.3.2 { - db2 cache flush - sqlite4_close db2 -} {SQLITE_BUSY} -do_test backup-4.3.3 { - sqlite4_errmsg db2 -} {unable to close due to unfinished backup operation} -do_test backup-4.3.4 { - B step 50 -} {SQLITE_DONE} -do_test backup-4.3.5 { - B finish -} {SQLITE_OK} - -do_test backup-4.4.1 { - set rc [catch {sqlite4_backup B db main db aux1}] - list $rc [sqlite4_errcode db] [sqlite4_errmsg db] -} {1 SQLITE_ERROR {source and destination must be distinct}} -db close -db2 close - -do_test backup-4.5.1 { - catch { forcedelete test.db } - sqlite4 db test.db - sqlite4 db2 :memory: - execsql { - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); - } - execsql { - PRAGMA page_size = 4096; - CREATE TABLE t2(a, b); - INSERT INTO t2 VALUES(3, 4); - } db2 - sqlite4_backup B db2 main db main -} {B} -do_test backup-4.5.2 { - B step 5000 -} {SQLITE_READONLY} -do_test backup-4.5.3 { - B finish -} {SQLITE_READONLY} - -db close -db2 close -# -# End of backup-5.* tests. -#--------------------------------------------------------------------- - -#--------------------------------------------------------------------- -# The following tests, backup-5.*, test that the backup works properly -# when the source database is modified during the backup. Test cases -# are organized as follows: -# -# backup-5.x.1.*: Nothing special. Modify the database mid-backup. -# -# backup-5.x.2.*: Modify the database mid-backup so that one or more -# pages are written out due to cache stress. Then -# rollback the transaction. -# -# backup-5.x.3.*: Database is vacuumed. -# -# backup-5.x.4.*: Database is vacuumed and the page-size modified. -# -# backup-5.x.5.*: Database is shrunk via incr-vacuum. -# -# Each test is run three times, in the following configurations: -# -# 1) Backing up file-to-file. The writer writes via an external pager. -# 2) Backing up file-to-file. The writer writes via the same pager as -# is used by the backup operation. -# 3) Backing up memory-to-file. -# -set iTest 0 -forcedelete bak.db-wal -foreach {writer file} {db test.db db3 test.db db :memory:} { - incr iTest - catch { delete_file bak.db } - sqlite4 db2 bak.db - catch { delete_file $file } - sqlite4 db $file - sqlite4 db3 $file - - do_test backup-5.$iTest.1.1 { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - COMMIT; - } - expr {[execsql {PRAGMA page_count}] > 10} - } {1} - do_test backup-5.$iTest.1.2 { - sqlite4_backup B db2 main db main - B step 5 - } {SQLITE_OK} - do_test backup-5.$iTest.1.3 { - execsql { UPDATE t1 SET a = a + 1 } $writer - B step 50 - } {SQLITE_DONE} - do_test backup-5.$iTest.1.4 { - B finish - } {SQLITE_OK} - integrity_check backup-5.$iTest.1.5 db2 - test_contents backup-5.$iTest.1.6 db main db2 main - - do_test backup-5.$iTest.2.1 { - execsql { - PRAGMA cache_size = 10; - BEGIN; - INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1; - COMMIT; - } - } {} - do_test backup-5.$iTest.2.2 { - sqlite4_backup B db2 main db main - B step 50 - } {SQLITE_OK} - do_test backup-5.$iTest.2.3 { - execsql { - BEGIN; - UPDATE t1 SET a = a + 1; - ROLLBACK; - } $writer - B step 5000 - } {SQLITE_DONE} - do_test backup-5.$iTest.2.4 { - B finish - } {SQLITE_OK} - integrity_check backup-5.$iTest.2.5 db2 - test_contents backup-5.$iTest.2.6 db main db2 main - - do_test backup-5.$iTest.3.1 { - execsql { UPDATE t1 SET b = randstr(1000,1000) } - } {} - do_test backup-5.$iTest.3.2 { - sqlite4_backup B db2 main db main - B step 50 - } {SQLITE_OK} - do_test backup-5.$iTest.3.3 { - execsql { VACUUM } $writer - B step 5000 - } {SQLITE_DONE} - do_test backup-5.$iTest.3.4 { - B finish - } {SQLITE_OK} - integrity_check backup-5.$iTest.3.5 db2 - test_contents backup-5.$iTest.3.6 db main db2 main - - do_test backup-5.$iTest.4.1 { - execsql { UPDATE t1 SET b = randstr(1000,1000) } - } {} - do_test backup-5.$iTest.4.2 { - sqlite4_backup B db2 main db main - B step 50 - } {SQLITE_OK} - do_test backup-5.$iTest.4.3 { - execsql { - PRAGMA page_size = 2048; - VACUUM; - } $writer - B step 5000 - } {SQLITE_DONE} - do_test backup-5.$iTest.4.4 { - B finish - } {SQLITE_OK} - integrity_check backup-5.$iTest.4.5 db2 - test_contents backup-5.$iTest.4.6 db main db2 main - - catch {db close} - catch {db2 close} - catch {db3 close} - catch { delete_file bak.db } - sqlite4 db2 bak.db - catch { delete_file $file } - sqlite4 db $file - sqlite4 db3 $file - do_test backup-5.$iTest.5.1 { - execsql { - PRAGMA auto_vacuum = incremental; - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - COMMIT; - } - } {} - do_test backup-5.$iTest.5.2 { - sqlite4_backup B db2 main db main - B step 8 - } {SQLITE_OK} - do_test backup-5.$iTest.5.3 { - execsql { - DELETE FROM t1; - PRAGMA incremental_vacuum; - } $writer - B step 50 - } {SQLITE_DONE} - do_test backup-5.$iTest.5.4 { - B finish - } {SQLITE_OK} - integrity_check backup-5.$iTest.5.5 db2 - test_contents backup-5.$iTest.5.6 db main db2 main - catch {db close} - catch {db2 close} - catch {db3 close} -} -# -# End of backup-5.* tests. -#--------------------------------------------------------------------- - -#--------------------------------------------------------------------- -# Test the sqlite4_backup_remaining() and backup_pagecount() APIs. -# -do_test backup-6.1 { - catch { forcedelete test.db } - catch { forcedelete test2.db } - sqlite4 db test.db - sqlite4 db2 test2.db - execsql { - BEGIN; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - COMMIT; - } -} {} -do_test backup-6.2 { - set nTotal [expr {[file size test.db]/1024}] - sqlite4_backup B db2 main db main - B step 1 -} {SQLITE_OK} -do_test backup-6.3 { - B pagecount -} $nTotal -do_test backup-6.4 { - B remaining -} [expr $nTotal-1] -do_test backup-6.5 { - B step 5 - list [B remaining] [B pagecount] -} [list [expr $nTotal-6] $nTotal] -do_test backup-6.6 { - execsql { CREATE TABLE t2(a PRIMARY KEY, b) } - B step 1 - list [B remaining] [B pagecount] -} [list [expr $nTotal-5] [expr $nTotal+2]] - -do_test backup-6.X { - B finish -} {SQLITE_OK} - -catch {db close} -catch {db2 close} - -#--------------------------------------------------------------------- -# Test cases backup-7.* test that SQLITE_BUSY and SQLITE_LOCKED errors -# are returned correctly: -# -# backup-7.1.*: Source database is externally locked (return SQLITE_BUSY). -# -# backup-7.2.*: Attempt to step the backup process while a -# write-transaction is underway on the source pager (return -# SQLITE_LOCKED). -# -# backup-7.3.*: Destination database is externally locked (return SQLITE_BUSY). -# -do_test backup-7.0 { - catch { forcedelete test.db } - catch { forcedelete test2.db } - sqlite4 db2 test2.db - sqlite4 db test.db - execsql { - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1; - } -} {} - -do_test backup-7.1.1 { - sqlite4_backup B db2 main db main - B step 5 -} {SQLITE_OK} -do_test backup-7.1.2 { - sqlite4 db3 test.db - execsql { BEGIN EXCLUSIVE } db3 - B step 5 -} {SQLITE_BUSY} -do_test backup-7.1.3 { - execsql { ROLLBACK } db3 - B step 5 -} {SQLITE_OK} -do_test backup-7.2.1 { - execsql { - BEGIN; - INSERT INTO t1 VALUES(1, 4); - } -} {} -do_test backup-7.2.2 { - B step 5000 -} {SQLITE_BUSY} -do_test backup-7.2.3 { - execsql { ROLLBACK } - B step 5000 -} {SQLITE_DONE} -do_test backup-7.2.4 { - B finish -} {SQLITE_OK} -test_contents backup-7.2.5 db main db2 main -integrity_check backup-7.3.6 db2 - -do_test backup-7.3.1 { - db2 close - db3 close - forcedelete test2.db - sqlite4 db2 test2.db - sqlite4 db3 test2.db - - sqlite4_backup B db2 main db main - execsql { BEGIN ; CREATE TABLE t2(a, b); } db3 - - B step 5 -} {SQLITE_BUSY} -do_test backup-7.3.2 { - execsql { COMMIT } db3 - B step 5000 -} {SQLITE_DONE} -do_test backup-7.3.3 { - B finish -} {SQLITE_OK} -test_contents backup-7.3.4 db main db2 main -integrity_check backup-7.3.5 db2 -catch { db2 close } -catch { db3 close } - -#----------------------------------------------------------------------- -# The following tests, backup-8.*, test attaching multiple backup -# processes to the same source database. Also, reading from the source -# database while a read transaction is active. -# -# These tests reuse the database "test.db" left over from backup-7.*. -# -do_test backup-8.1 { - catch { forcedelete test2.db } - catch { forcedelete test3.db } - sqlite4 db2 test2.db - sqlite4 db3 test3.db - - sqlite4_backup B2 db2 main db main - sqlite4_backup B3 db3 main db main - list [B2 finish] [B3 finish] -} {SQLITE_OK SQLITE_OK} -do_test backup-8.2 { - sqlite4_backup B3 db3 main db main - sqlite4_backup B2 db2 main db main - list [B2 finish] [B3 finish] -} {SQLITE_OK SQLITE_OK} -do_test backup-8.3 { - sqlite4_backup B2 db2 main db main - sqlite4_backup B3 db3 main db main - B2 step 5 -} {SQLITE_OK} -do_test backup-8.4 { - execsql { - BEGIN; - SELECT * FROM sqlite_master; - } - B3 step 5 -} {SQLITE_OK} -do_test backup-8.5 { - list [B3 step 5000] [B3 finish] -} {SQLITE_DONE SQLITE_OK} -do_test backup-8.6 { - list [B2 step 5000] [B2 finish] -} {SQLITE_DONE SQLITE_OK} -test_contents backup-8.7 db main db2 main -test_contents backup-8.8 db main db3 main -do_test backup-8.9 { - execsql { PRAGMA lock_status } -} {main shared temp closed} -do_test backup-8.10 { - execsql COMMIT -} {} -catch { db2 close } -catch { db3 close } - -#----------------------------------------------------------------------- -# The following tests, backup-9.*, test that: -# -# * Passing 0 as an argument to sqlite4_backup_step() means no pages -# are backed up (backup-9.1.*), and -# * Passing a negative value as an argument to sqlite4_backup_step() means -# all pages are backed up (backup-9.2.*). -# -# These tests reuse the database "test.db" left over from backup-7.*. -# -do_test backup-9.1.1 { - sqlite4 db2 test2.db - sqlite4_backup B db2 main db main - B step 1 -} {SQLITE_OK} -do_test backup-9.1.2 { - set nRemaining [B remaining] - expr {$nRemaining>100} -} {1} -do_test backup-9.1.3 { - B step 0 -} {SQLITE_OK} -do_test backup-9.1.4 { - B remaining -} $nRemaining - -do_test backup-9.2.1 { - B step -1 -} {SQLITE_DONE} -do_test backup-9.2.2 { - B remaining -} {0} -do_test backup-9.2.3 { - B finish -} {SQLITE_OK} -catch {db2 close} - -ifcapable memorymanage { - db close - forcedelete test.db - forcedelete bak.db - - sqlite4 db test.db - sqlite4 db2 test.db - sqlite4 db3 bak.db - - do_test backup-10.1.1 { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 VALUES(2, randstr(1000,1000)); - INSERT INTO t1 VALUES(3, randstr(1000,1000)); - INSERT INTO t1 VALUES(4, randstr(1000,1000)); - INSERT INTO t1 VALUES(5, randstr(1000,1000)); - CREATE INDEX i1 ON t1(a, b); - COMMIT; - } - } {} - do_test backup-10.1.2 { - sqlite4_backup B db3 main db2 main - B step 5 - } {SQLITE_OK} - do_test backup-10.1.3 { - execsql { - UPDATE t1 SET b = randstr(500,500); - } - } {} - sqlite4_release_memory [expr 1024*1024] - do_test backup-10.1.3 { - B step 50 - } {SQLITE_DONE} - do_test backup-10.1.4 { - B finish - } {SQLITE_OK} - do_test backup-10.1.5 { - execsql { PRAGMA integrity_check } db3 - } {ok} - - db2 close - db3 close -} - - -#----------------------------------------------------------------------- -# Test that if the database is written to via the same database handle being -# used as the source by a backup operation: -# -# 10.1.*: If the db is in-memory, the backup is restarted. -# 10.2.*: If the db is a file, the backup is not restarted. -# -db close -forcedelete test.db test.db-journal -foreach {tn file rc} { - 1 test.db SQLITE_DONE - 2 :memory: SQLITE_OK -} { - do_test backup-10.$tn.1 { - sqlite4 db $file - execsql { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB); - BEGIN; - INSERT INTO t1 VALUES(NULL, randomblob(200)); - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1; - COMMIT; - SELECT count(*) FROM t1; - } - } {256} - - do_test backup-10.$tn.2 { - set pgs [execsql {pragma page_count}] - expr {$pgs > 50 && $pgs < 75} - } {1} - - do_test backup-10.$tn.3 { - forcedelete bak.db bak.db-journal - sqlite4 db2 bak.db - sqlite4_backup B db2 main db main - B step 50 - } {SQLITE_OK} - - do_test backup-10.$tn.4 { - execsql { UPDATE t1 SET b = randomblob(200) WHERE a IN (1, 250) } - } {} - - do_test backup-10.$tn.5 { - B step 50 - } $rc - - do_test backup-10.$tn.6 { - B finish - } {SQLITE_OK} - - db2 close -} - -finish_test DELETED test/backup2.test Index: test/backup2.test ================================================================== --- test/backup2.test +++ /dev/null @@ -1,186 +0,0 @@ -# 2009 February 4 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this file is testing the "backup" and "restore" methods -# of the TCL interface - methods which are based on the -# sqlite4_backup_XXX API. -# -# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -do_not_use_codec - -ifcapable !trigger||!view { finish_test ; return } - -# Fill a database with test data. -# -do_test backup2-1 { - db eval { - CREATE TABLE t1(x); - INSERT INTO t1 VALUES(randstr(8000,8000)); - INSERT INTO t1 VALUES(randstr(8000,8000)); - INSERT INTO t1 VALUES(randstr(8000,8000)); - INSERT INTO t1 VALUES(randstr(8000,8000)); - INSERT INTO t1 VALUES(randstr(8000,8000)); - CREATE VIEW v1 AS SELECT substr(x,10,10) FROM t1; - CREATE TABLE t2(a,b); - INSERT INTO t2 VALUES(1,2); - INSERT INTO t2 VALUES(2,4); - INSERT INTO t2 SELECT a+2, (a+2)*2 FROM t2; - INSERT INTO t2 SELECT a+4, (a+4)*2 FROM t2; - INSERT INTO t2 SELECT a+8, (a+8)*2 FROM t2; - INSERT INTO t2 SELECT a+16, (a+16)*2 FROM t2; - INSERT INTO t2 SELECT a+32, (a+32)*2 FROM t2; - INSERT INTO t2 SELECT a+64, (a+64)*2 FROM t2; - INSERT INTO t2 SELECT a+128, (a+128)*2 FROM t2; - CREATE INDEX t2i1 ON t2(a,b); - CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN - SELECT 'hello'; - END; - ANALYZE; - PRAGMA integrity_check; - } -} {ok} - -# Remember a check-sum on the database file. -# -unset -nocomplain cksum -set cksum [dbcksum db main] - -# Make a backup of the test data. Verify that the backup copy -# is identical to the original. -# -do_test backup2-2 { - forcedelete bu1.db - db backup bu1.db - sqlite4 db2 bu1.db - dbcksum db2 main -} $cksum - -# Delete the original. Restore from backup. Verify the content is -# unchanged. -# -do_test backup2-3.1 { - db close - forcedelete test.db test.db-journal - sqlite4 db test.db - db2 eval {BEGIN EXCLUSIVE} - set rc [catch {db restore bu1.db} res] - lappend rc $res - db2 eval {ROLLBACK} - set rc -} {1 {restore failed: source database busy}} -do_test backup2-3.2 { - db close - forcedelete test.db test.db-journal - sqlite4 db test.db - db restore bu1.db - dbcksum db main -} $cksum - -# Use alternative databases - other than "main". -# -do_test backup2-4 { - db restore temp bu1.db - dbcksum db temp -} $cksum -do_test backup2-5 { - db2 close - forcedelete bu1.db bu2.db - db backup temp bu2.db - sqlite4 db2 bu2.db - dbcksum db2 main -} $cksum - -# Try to backup to a readonly file. -# -do_test backup2-6 { - db2 close - catch {file attributes bu2.db -permissions r--------} - catch {file attributes bu2.db -readonly 1} - set rc [catch {db backup temp bu2.db} res] - lappend rc $res -} {1 {backup failed: attempt to write a readonly database}} - -# Try to backup to something that is not a database file. -# -do_test backup2-7 { - catch {file attributes bu2.db -readonly 0} - catch {file attributes bu2.db -permissions rw-------} - set out [open bu2.db w] - puts $out "This is not a valid database file" - close $out - set rc [catch {db backup temp bu2.db} res] - lappend rc $res -} {1 {backup failed: file is encrypted or is not a database}} - -# Try to backup database that does not exist -# -do_test backup2-8 { - forcedelete bu1.db - set rc [catch {db backup aux1 bu1.db} res] - lappend rc $res -} {1 {backup failed: unknown database aux1}} - -# Invalid syntax on the backup method -# -do_test backup2-9 { - set rc [catch {db backup} res] - lappend rc $res -} {1 {wrong # args: should be "db backup ?DATABASE? FILENAME"}} - -# Try to restore from an unreadable file. -# -if {$tcl_platform(platform)=="windows"} { - set msg {cannot open source database: unable to open database file} -} elseif {$tcl_platform(os)=="OpenBSD"} { - set msg {restore failed: file is encrypted or is not a database} -} else { - set msg {cannot open source database: disk I/O error} -} -do_test backup2-10 { - forcedelete bu3.db - file mkdir bu3.db - set rc [catch {db restore temp bu3.db} res] - lappend rc $res -} [list 1 $msg] - -# Try to restore from something that is not a database file. -# -do_test backup2-11 { - set rc [catch {db restore temp bu2.db} res] - lappend rc $res -} {1 {restore failed: file is encrypted or is not a database}} - -# Try to restore a database that does not exist -# -do_test backup2-12 { - set rc [catch {db restore aux1 bu2.db} res] - lappend rc $res -} {1 {restore failed: unknown database aux1}} -do_test backup2-13 { - forcedelete bu4.db - set rc [catch {db restore bu4.db} res] - lappend rc $res -} {1 {cannot open source database: unable to open database file}} - -# Invalid syntax on the restore method -# -do_test backup2-14 { - set rc [catch {db restore} res] - lappend rc $res -} {1 {wrong # args: should be "db restore ?DATABASE? FILENAME"}} - -forcedelete bu1.db bu2.db bu3.db bu4.db - -finish_test DELETED test/backup_ioerr.test Index: test/backup_ioerr.test ================================================================== --- test/backup_ioerr.test +++ /dev/null @@ -1,286 +0,0 @@ -# 2009 January 30 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this file is testing the handling of IO errors by the -# sqlite4_backup_XXX APIs. -# -# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -proc data_checksum {db file} { - $db one "SELECT md5sum(a, b) FROM ${file}.t1" -} -proc test_contents {name db1 file1 db2 file2} { - $db2 eval {select * from sqlite_master} - $db1 eval {select * from sqlite_master} - set checksum [data_checksum $db2 $file2] - uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum] -} - -#-------------------------------------------------------------------- -# This proc creates a database of approximately 290 pages. Depending -# on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.* -# verify nothing more than this assumption. -# -proc populate_database {db {xtra_large 0}} { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1; - CREATE INDEX i1 ON t1(b); - COMMIT; - } $db - if {$xtra_large} { - execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db - } -} -do_test backup_ioerr-1.1 { - populate_database db - set nPage [expr {[file size test.db] / 1024}] - expr {$nPage>130 && $nPage<160} -} {1} -do_test backup_ioerr-1.2 { - expr {[file size test.db] > $sqlite_pending_byte} -} {1} -do_test backup_ioerr-1.3 { - db close - forcedelete test.db -} {} - -# Turn off IO error simulation. -# -proc clear_ioerr_simulation {} { - set ::sqlite_io_error_hit 0 - set ::sqlite_io_error_hardhit 0 - set ::sqlite_io_error_pending 0 - set ::sqlite_io_error_persist 0 -} - -#-------------------------------------------------------------------- -# The following procedure runs with SQLite's IO error simulation -# enabled. -# -# 1) Start with a reasonably sized database. One that includes the -# pending-byte (locking) page. -# -# 2) Open a backup process. Set the cache-size for the destination -# database to 10 pages only. -# -# 3) Step the backup process N times to partially backup the database -# file. If an IO error is reported, then the backup process is -# concluded with a call to backup_finish(). -# -# If an IO error occurs, verify that: -# -# * the call to backup_step() returns an SQLITE_IOERR_XXX error code. -# -# * after the failed call to backup_step() but before the call to -# backup_finish() the destination database handle error code and -# error message remain unchanged. -# -# * the call to backup_finish() returns an SQLITE_IOERR_XXX error code. -# -# * following the call to backup_finish(), the destination database -# handle has been populated with an error code and error message. -# -# 4) Write to the database via the source database connection. Check -# that: -# -# * If an IO error occurs while writing the source database, the -# write operation should report an IO error. The backup should -# proceed as normal. -# -# * If an IO error occurs while updating the backup, the write -# operation should proceed normally. The error should be reported -# from the next call to backup_step() (in step 5 of this test -# procedure). -# -# 5) Step the backup process to finish the backup. If an IO error is -# reported, then the backup process is concluded with a call to -# backup_finish(). -# -# Test that if an IO error occurs, or if one occured while updating -# the backup database during step 4, then the conditions listed -# under step 3 are all true. -# -# 6) Finish the backup process. -# -# * If the backup succeeds (backup_finish() returns SQLITE_OK), then -# the contents of the backup database should match that of the -# source database. -# -# * If the backup fails (backup_finish() returns other than SQLITE_OK), -# then the contents of the backup database should be as they were -# before the operation was started. -# -# The following factors are varied: -# -# * Destination database is initially larger than the source database, OR -# * Destination database is initially smaller than the source database. -# -# * IO errors are transient, OR -# * IO errors are persistent. -# -# * Destination page-size is smaller than the source. -# * Destination page-size is the same as the source. -# * Destination page-size is larger than the source. -# - -set iTest 1 -foreach bPersist {0 1} { -foreach iDestPagesize {512 1024 4096} { -foreach zSetupBak [list "" {populate_database ddb 1}] { - - incr iTest - set bStop 0 -for {set iError 1} {$bStop == 0} {incr iError} { - # Disable IO error simulation. - clear_ioerr_simulation - - catch { ddb close } - catch { sdb close } - catch { forcedelete test.db } - catch { forcedelete bak.db } - - # Open the source and destination databases. - sqlite4 sdb test.db - sqlite4 ddb bak.db - - # Step 1: Populate the source and destination databases. - populate_database sdb - ddb eval "PRAGMA page_size = $iDestPagesize" - ddb eval "PRAGMA cache_size = 10" - eval $zSetupBak - - # Step 2: Open the backup process. - sqlite4_backup B ddb main sdb main - - # Enable IO error simulation. - set ::sqlite_io_error_pending $iError - set ::sqlite_io_error_persist $bPersist - - # Step 3: Partially backup the database. If an IO error occurs, check - # a few things then skip to the next iteration of the loop. - # - set rc [B step 100] - if {$::sqlite_io_error_hardhit} { - - do_test backup_ioerr-$iTest.$iError.1 { - string match SQLITE_IOERR* $rc - } {1} - do_test backup_ioerr-$iTest.$iError.2 { - list [sqlite4_errcode ddb] [sqlite4_errmsg ddb] - } {SQLITE_OK {not an error}} - - set rc [B finish] - do_test backup_ioerr-$iTest.$iError.3 { - string match SQLITE_IOERR* $rc - } {1} - - do_test backup_ioerr-$iTest.$iError.4 { - sqlite4_errmsg ddb - } {disk I/O error} - - clear_ioerr_simulation - sqlite4 ddb bak.db - integrity_check backup_ioerr-$iTest.$iError.5 ddb - - continue - } - - # No IO error was encountered during step 3. Check that backup_step() - # returned SQLITE_OK before proceding. - do_test backup_ioerr-$iTest.$iError.6 { - expr {$rc eq "SQLITE_OK"} - } {1} - - # Step 4: Write to the source database. - set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb] - - if {[lindex $rc 0] && $::sqlite_io_error_persist==0} { - # The IO error occured while updating the source database. In this - # case the backup should be able to continue. - set rc [B step 5000] - if { $rc != "SQLITE_IOERR_UNLOCK" } { - do_test backup_ioerr-$iTest.$iError.7 { - list [B step 5000] [B finish] - } {SQLITE_DONE SQLITE_OK} - - clear_ioerr_simulation - test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main - integrity_check backup_ioerr-$iTest.$iError.9 ddb - } else { - do_test backup_ioerr-$iTest.$iError.10 { - B finish - } {SQLITE_IOERR_UNLOCK} - } - - clear_ioerr_simulation - sqlite4 ddb bak.db - integrity_check backup_ioerr-$iTest.$iError.11 ddb - - continue - } - - # Step 5: Finish the backup operation. If an IO error occurs, check that - # it is reported correctly and skip to the next iteration of the loop. - # - set rc [B step 5000] - if {$rc != "SQLITE_DONE"} { - do_test backup_ioerr-$iTest.$iError.12 { - string match SQLITE_IOERR* $rc - } {1} - do_test backup_ioerr-$iTest.$iError.13 { - list [sqlite4_errcode ddb] [sqlite4_errmsg ddb] - } {SQLITE_OK {not an error}} - - set rc [B finish] - do_test backup_ioerr-$iTest.$iError.14 { - string match SQLITE_IOERR* $rc - } {1} - do_test backup_ioerr-$iTest.$iError.15 { - sqlite4_errmsg ddb - } {disk I/O error} - - clear_ioerr_simulation - sqlite4 ddb bak.db - integrity_check backup_ioerr-$iTest.$iError.16 ddb - - continue - } - - # The backup was successfully completed. - # - do_test backup_ioerr-$iTest.$iError.17 { - list [set rc] [B finish] - } {SQLITE_DONE SQLITE_OK} - - clear_ioerr_simulation - sqlite4 sdb test.db - sqlite4 ddb bak.db - - test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main - integrity_check backup_ioerr-$iTest.$iError.19 ddb - - set bStop [expr $::sqlite_io_error_pending<=0] -}}}} - -catch { sdb close } -catch { ddb close } -finish_test DELETED test/backup_malloc.test Index: test/backup_malloc.test ================================================================== --- test/backup_malloc.test +++ /dev/null @@ -1,87 +0,0 @@ -# 2009 January 30 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this file is testing the handling of OOM errors by the -# sqlite4_backup_XXX APIs. -# -# $Id: backup_malloc.test,v 1.2 2009/02/04 22:46:47 drh Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -source $testdir/malloc_common.tcl - -do_malloc_test backup_malloc-1 -tclprep { - execsql { - PRAGMA cache_size = 10; - BEGIN; - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, randstr(1000,1000)); - INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1; - INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1; - CREATE INDEX i1 ON t1(b); - COMMIT; - } - sqlite4 db2 test2.db - execsql { PRAGMA cache_size = 10 } db2 -} -tclbody { - - # Create a backup object. - # - set rc [catch {sqlite4_backup B db2 main db main}] - if {$rc && [sqlite4_errcode db2] == "SQLITE_NOMEM"} { - error "out of memory" - } - - # Run the backup process some. - # - set rc [B step 50] - if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} { - error "out of memory" - } - - # Update the database. - # - execsql { UPDATE t1 SET a = a + 1 } - - # Finish doing the backup. - # - set rc [B step 5000] - if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} { - error "out of memory" - } - - # Finalize the backup. - B finish -} -cleanup { - catch { B finish } - catch { db2 close } -} - -do_malloc_test backup_malloc-2 -tclprep { - sqlite4 db2 test2.db -} -tclbody { - set rc [catch {sqlite4_backup B db2 temp db main}] - set errcode [sqlite4_errcode db2] - if {$rc && ($errcode == "SQLITE_NOMEM" || $errcode == "SQLITE_IOERR_NOMEM")} { - error "out of memory" - } -} -cleanup { - catch { B finish } - db2 close -} - -finish_test DELETED test/incrblob.test Index: test/incrblob.test ================================================================== --- test/incrblob.test +++ /dev/null @@ -1,690 +0,0 @@ -# 2007 May 1 -# -# 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. -# -#*********************************************************************** -# -# $Id: incrblob.test,v 1.24 2009/06/19 22:23:42 drh Exp $ -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -ifcapable {!autovacuum || !pragma || !incrblob} { - finish_test - return -} - -do_test incrblob-1.1 { - execsql { - CREATE TABLE blobs(k PRIMARY KEY, v BLOB); - INSERT INTO blobs VALUES('one', X'0102030405060708090A'); - INSERT INTO blobs VALUES('two', X'0A090807060504030201'); - } -} {} - -do_test incrblob-1.2.1 { - set ::blob [db incrblob blobs v 1] - string match incrblob_* $::blob -} {1} -unset -nocomplain data -do_test incrblob-1.2.2 { - binary scan [read $::blob] c* data - set data -} {1 2 3 4 5 6 7 8 9 10} -do_test incrblob-1.2.3 { - seek $::blob 0 - puts -nonewline $::blob "1234567890" - flush $::blob -} {} -do_test incrblob-1.2.4 { - seek $::blob 0 - binary scan [read $::blob] c* data - set data -} {49 50 51 52 53 54 55 56 57 48} -do_test incrblob-1.2.5 { - close $::blob -} {} -do_test incrblob-1.2.6 { - execsql { - SELECT v FROM blobs WHERE rowid = 1; - } -} {1234567890} - -#-------------------------------------------------------------------- -# Test cases incrblob-1.3.X check that it is possible to read and write -# regions of a blob that lie on overflow pages. -# -do_test incrblob-1.3.1 { - set ::str "[string repeat . 10000]" - execsql { - INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str); - } -} {} - -do_test incrblob-1.3.2 { - set ::blob [db incrblob blobs v 3] - seek $::blob 8500 - read $::blob 10 -} {..........} -do_test incrblob-1.3.3 { - seek $::blob 8500 - puts -nonewline $::blob 1234567890 -} {} -do_test incrblob-1.3.4 { - seek $::blob 8496 - read $::blob 10 -} {....123456} -do_test incrblob-1.3.10 { - close $::blob -} {} - -#------------------------------------------------------------------------ -# incrblob-2.*: -# -# Test that the following operations use ptrmap pages to reduce -# unnecessary reads: -# -# * Reading near the end of a blob, -# * Writing near the end of a blob, and -# * SELECT a column value that is located on an overflow page. -# -proc nRead {db} { - set bt [btree_from_db $db] - db_enter $db - array set stats [btree_pager_stats $bt] - db_leave $db - return $stats(read) -} -proc nWrite {db} { - set bt [btree_from_db $db] - db_enter $db - array set stats [btree_pager_stats $bt] - db_leave $db - return $stats(write) -} - -sqlite4_soft_heap_limit 0 - -foreach AutoVacuumMode [list 0 1] { - - if {$AutoVacuumMode>0} { - ifcapable !autovacuum { - break - } - } - - db close - forcedelete test.db test.db-journal - - sqlite4 db test.db - execsql "PRAGMA auto_vacuum = $AutoVacuumMode" - - do_test incrblob-2.$AutoVacuumMode.1 { - set ::str [string repeat abcdefghij 2900] - execsql { - BEGIN; - CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER); - DELETE FROM blobs; - INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45); - COMMIT; - } - expr [file size test.db]/1024 - } [expr 31 + $AutoVacuumMode] - - ifcapable autovacuum { - do_test incrblob-2.$AutoVacuumMode.2 { - execsql { - PRAGMA auto_vacuum; - } - } $AutoVacuumMode - } - - do_test incrblob-2.$AutoVacuumMode.3 { - # Open and close the db to make sure the page cache is empty. - db close - sqlite4 db test.db - - # Read the last 20 bytes of the blob via a blob handle. - set ::blob [db incrblob blobs v 1] - seek $::blob -20 end - set ::fragment [read $::blob] - close $::blob - - # If the database is not in auto-vacuum mode, the whole of - # the overflow-chain must be scanned. In auto-vacuum mode, - # sqlite uses the ptrmap pages to avoid reading the other pages. - # - nRead db - } [expr $AutoVacuumMode ? 4 : 30] - - do_test incrblob-2.$AutoVacuumMode.4 { - string range [db one {SELECT v FROM blobs}] end-19 end - } $::fragment - - do_test incrblob-2.$AutoVacuumMode.5 { - # Open and close the db to make sure the page cache is empty. - db close - sqlite4 db test.db - - # Write the second-to-last 20 bytes of the blob via a blob handle. - # - set ::blob [db incrblob blobs v 1] - seek $::blob -40 end - puts -nonewline $::blob "1234567890abcdefghij" - flush $::blob - - # If the database is not in auto-vacuum mode, the whole of - # the overflow-chain must be scanned. In auto-vacuum mode, - # sqlite uses the ptrmap pages to avoid reading the other pages. - # - nRead db - } [expr $AutoVacuumMode ? 4 : 30] - - # Pages 1 (the write-counter) and 32 (the blob data) were written. - do_test incrblob-2.$AutoVacuumMode.6 { - close $::blob - nWrite db - } 2 - - do_test incrblob-2.$AutoVacuumMode.7 { - string range [db one {SELECT v FROM blobs}] end-39 end-20 - } "1234567890abcdefghij" - - do_test incrblob-2.$AutoVacuumMode.8 { - # Open and close the db to make sure the page cache is empty. - db close - sqlite4 db test.db - - execsql { SELECT i FROM blobs } - } {45} - - do_test incrblob-2.$AutoVacuumMode.9 { - nRead db - } [expr $AutoVacuumMode ? 4 : 30] -} -sqlite4_soft_heap_limit $cmdlinearg(soft-heap-limit) - -#------------------------------------------------------------------------ -# incrblob-3.*: -# -# Test the outcome of trying to write to a read-only blob handle. -# -do_test incrblob-3.1 { - set ::blob [db incrblob -readonly blobs v 1] - seek $::blob -40 end - read $::blob 20 -} "1234567890abcdefghij" -do_test incrblob-3.2 { - seek $::blob 0 - set rc [catch { - puts -nonewline $::blob "helloworld" - } msg] - close $::blob - list $rc $msg -} "1 {channel \"$::blob\" wasn't opened for writing}" - -do_test incrblob-3.3 { - set ::blob [db incrblob -readonly blobs v 1] - seek $::blob -40 end - read $::blob 20 -} "1234567890abcdefghij" -do_test incrblob-3.4 { - set rc [catch { - sqlite4_blob_write $::blob 20 "qwertyuioplkjhgfds" - } msg] - list $rc $msg -} {1 SQLITE_READONLY} -catch {close $::blob} - -#------------------------------------------------------------------------ -# incrblob-4.*: -# -# Try a couple of error conditions: -# -# 4.1 - Attempt to open a row that does not exist. -# 4.2 - Attempt to open a column that does not exist. -# 4.3 - Attempt to open a table that does not exist. -# 4.4 - Attempt to open a database that does not exist. -# -# 4.5 - Attempt to open an integer -# 4.6 - Attempt to open a real value -# 4.7 - Attempt to open an SQL null -# -# 4.8 - Attempt to open an indexed column for writing -# 4.9 - Attempt to open an indexed column for reading (this works) -# -# 4.11 - Attempt to open a column of a view. -# 4.12 - Attempt to open a column of a virtual table. -# -do_test incrblob-4.1 { - set rc [catch { - set ::blob [db incrblob blobs v 2] - } msg ] - list $rc $msg -} {1 {no such rowid: 2}} -do_test incrblob-4.2 { - set rc [catch { - set ::blob [db incrblob blobs blue 1] - } msg ] - list $rc $msg -} {1 {no such column: "blue"}} -do_test incrblob-4.3 { - set rc [catch { - set ::blob [db incrblob nosuchtable blue 1] - } msg ] - list $rc $msg -} {1 {no such table: main.nosuchtable}} -do_test incrblob-4.4 { - set rc [catch { - set ::blob [db incrblob nosuchdb blobs v 1] - } msg ] - list $rc $msg -} {1 {no such table: nosuchdb.blobs}} - -do_test incrblob-4.5 { - set rc [catch { - set ::blob [db incrblob blobs i 1] - } msg ] - list $rc $msg -} {1 {cannot open value of type integer}} -do_test incrblob-4.6 { - execsql { - INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL); - } - set rc [catch { - set ::blob [db incrblob blobs v 2] - } msg ] - list $rc $msg -} {1 {cannot open value of type real}} -do_test incrblob-4.7 { - set rc [catch { - set ::blob [db incrblob blobs i 2] - } msg ] - list $rc $msg -} {1 {cannot open value of type null}} - -do_test incrblob-4.8.1 { - execsql { - INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world'); - } - set rc [catch { - set ::blob [db incrblob blobs k 3] - } msg ] - list $rc $msg -} {1 {cannot open indexed column for writing}} -do_test incrblob-4.8.2 { - execsql { - CREATE TABLE t3(a INTEGER PRIMARY KEY, b); - INSERT INTO t3 VALUES(1, 2); - } - set rc [catch { - set ::blob [db incrblob -readonly t3 a 1] - } msg ] - list $rc $msg -} {1 {cannot open value of type null}} -do_test incrblob-4.8.3 { - set rc [catch { - set ::blob [db incrblob -readonly t3 rowid 1] - } msg ] - list $rc $msg -} {1 {no such column: "rowid"}} - -do_test incrblob-4.9.1 { - set rc [catch { - set ::blob [db incrblob -readonly blobs k 3] - } msg] -} {0} -do_test incrblob-4.9.2 { - binary scan [read $::blob] c* c - close $::blob - set c -} {1 2 3 4 5 6 7 8 9} - -do_test incrblob-4.10 { - set ::blob [db incrblob -readonly blobs k 3] - set rc [catch { sqlite4_blob_read $::blob 10 100 } msg] - list $rc $msg -} {1 SQLITE_ERROR} -do_test incrblob-4.10.2 { - close $::blob -} {} - -ifcapable view { - do_test incrblob-4.11 { - execsql { CREATE VIEW blobs_view AS SELECT k, v, i FROM blobs } - set rc [catch { db incrblob blobs_view v 3 } msg] - list $rc $msg - } {1 {cannot open view: blobs_view}} -} -ifcapable vtab { - register_echo_module [sqlite4_connection_pointer db] - do_test incrblob-4.12 { - execsql { CREATE VIRTUAL TABLE blobs_echo USING echo(blobs) } - set rc [catch { db incrblob blobs_echo v 3 } msg] - list $rc $msg - } {1 {cannot open virtual table: blobs_echo}} -} - - -#------------------------------------------------------------------------ -# incrblob-5.*: -# -# Test that opening a blob in an attached database works. -# -ifcapable attach { - do_test incrblob-5.1 { - forcedelete test2.db test2.db-journal - set ::size [expr [file size [info script]]] - execsql { - ATTACH 'test2.db' AS aux; - CREATE TABLE aux.files(name, text); - INSERT INTO aux.files VALUES('this one', zeroblob($::size)); - } - set fd [db incrblob aux files text 1] - fconfigure $fd -translation binary - set fd2 [open [info script]] - fconfigure $fd2 -translation binary - puts -nonewline $fd [read $fd2] - close $fd - close $fd2 - set ::text [db one {select text from aux.files}] - string length $::text - } [file size [info script]] - do_test incrblob-5.2 { - set fd2 [open [info script]] - fconfigure $fd2 -translation binary - set ::data [read $fd2] - close $fd2 - set ::data - } $::text -} - -# free memory -unset -nocomplain ::data -unset -nocomplain ::text - -#------------------------------------------------------------------------ -# incrblob-6.*: -# -# Test that opening a blob for write-access is impossible if -# another connection has the database RESERVED lock. -# -# Then test that blob writes that take place inside of a -# transaction are not visible to external connections until -# after the transaction is commited and the blob channel -# closed. -# -# This test does not work with the "memsubsys1" configuration. -# Permutation memsubsys1 configures a very small static allocation -# for use as page-cache memory. This causes SQLite to upgrade -# to an exclusive lock when writing earlier than usual, which -# makes some of these tests fail. -# -sqlite4_soft_heap_limit 0 -if {[permutation] != "memsubsys1"} { - do_test incrblob-6.1 { - sqlite4 db2 test.db - execsql { - BEGIN; - INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection'); - } db2 - } {} - do_test incrblob-6.2 { - execsql { - SELECT rowid FROM blobs - } - } {1 2 3} - do_test incrblob-6.3 { - set rc [catch { - db incrblob blobs v 1 - } msg] - list $rc $msg - } {1 {database is locked}} - do_test incrblob-6.4 { - set rc [catch { - db incrblob blobs v 3 - } msg] - list $rc $msg - } {1 {database is locked}} - do_test incrblob-6.5 { - set ::blob [db incrblob -readonly blobs v 3] - read $::blob - } {hello} - do_test incrblob-6.6 { - close $::blob - } {} - - do_test incrblob-6.7 { - set ::blob [db2 incrblob blobs i 4] - gets $::blob - } {connection} - do_test incrblob-6.8 { - tell $::blob - } {10} - do_test incrblob-6.9 { - seek $::blob 0 - puts -nonewline $::blob "invocation" - flush $::blob - } {} - - # At this point rollback should be illegal (because - # there is an open blob channel). But commit is also illegal because - # the open blob is read-write. - # - do_test incrblob-6.10 { - catchsql { - ROLLBACK; - } db2 - } {1 {cannot rollback transaction - SQL statements in progress}} - do_test incrblob-6.11 { - catchsql { - COMMIT; - } db2 - } {1 {cannot commit transaction - SQL statements in progress}} - - do_test incrblob-6.12 { - execsql { - SELECT * FROM blobs WHERE rowid = 4; - } - } {} - do_test incrblob-6.13 { - close $::blob - } {} - do_test incrblob-6.14 { - catchsql { - COMMIT; - } db2 - } {0 {}} - do_test incrblob-6.15 { - execsql { - SELECT * FROM blobs WHERE rowid = 4; - } - } {a different invocation} - db2 close -} -sqlite4_soft_heap_limit $cmdlinearg(soft-heap-limit) - -#----------------------------------------------------------------------- -# The following tests verify the behaviour of the incremental IO -# APIs in the following cases: -# -# 7.1 A row that containing an open blob is modified. -# -# 7.2 A CREATE TABLE requires that an overflow page that is part -# of an open blob is moved. -# -# 7.3 An INCREMENTAL VACUUM moves an overflow page that is part -# of an open blob. -# -# In the first case above, correct behaviour is for all subsequent -# read/write operations on the blob-handle to return SQLITE_ABORT. -# More accurately, blob-handles are invalidated whenever the table -# they belong to is written to. -# -# The second two cases have no external effect. They are testing -# that the internal cache of overflow page numbers is correctly -# invalidated. -# -do_test incrblob-7.1.0 { - execsql { - BEGIN; - DROP TABLE blobs; - CREATE TABLE t1 (a, b, c, d BLOB); - INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4); - COMMIT; - } -} {} - -foreach {tn arg} {1 "" 2 -readonly} { - - execsql { - UPDATE t1 SET d = zeroblob(10000); - } - - do_test incrblob-7.1.$tn.1 { - set ::b [eval db incrblob $arg t1 d 1] - binary scan [sqlite4_blob_read $::b 5000 5] c* c - set c - } {0 0 0 0 0} - do_test incrblob-7.1.$tn.2 { - execsql { - UPDATE t1 SET d = 15; - } - } {} - do_test incrblob-7.1.$tn.3 { - set rc [catch { sqlite4_blob_read $::b 5000 5 } msg] - list $rc $msg - } {1 SQLITE_ABORT} - do_test incrblob-7.1.$tn.4 { - execsql { - SELECT d FROM t1; - } - } {15} - do_test incrblob-7.1.$tn.5 { - set rc [catch { close $::b } msg] - list $rc $msg - } {0 {}} - do_test incrblob-7.1.$tn.6 { - execsql { - SELECT d FROM t1; - } - } {15} - -} - -set fd [open [info script]] -fconfigure $fd -translation binary -set ::data [read $fd 14000] -close $fd - -db close -forcedelete test.db test.db-journal -sqlite4 db test.db - -do_test incrblob-7.2.1 { - execsql { - PRAGMA auto_vacuum = "incremental"; - CREATE TABLE t1(a INTEGER PRIMARY KEY, b); -- root@page3 - INSERT INTO t1 VALUES(123, $::data); - } - set ::b [db incrblob -readonly t1 b 123] - fconfigure $::b -translation binary - read $::b -} $::data -do_test incrblob-7.2.2 { - execsql { - CREATE TABLE t2(a INTEGER PRIMARY KEY, b); -- root@page4 - } - seek $::b 0 - read $::b -} $::data -do_test incrblob-7.2.3 { - close $::b - execsql { - SELECT rootpage FROM sqlite_master; - } -} {3 4} - -set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]" -do_test incrblob-7.3.1 { - execsql { - INSERT INTO t2 VALUES(456, $::otherdata); - } - set ::b [db incrblob -readonly t2 b 456] - fconfigure $::b -translation binary - read $::b -} $::otherdata -do_test incrblob-7.3.2 { - expr [file size test.db]/1024 -} 30 -do_test incrblob-7.3.3 { - execsql { - DELETE FROM t1 WHERE a = 123; - PRAGMA INCREMENTAL_VACUUM(0); - } - seek $::b 0 - read $::b -} $::otherdata - -# Attempt to write on a read-only blob. Make sure the error code -# gets set. Ticket #2464. -# -do_test incrblob-7.4 { - set rc [catch {sqlite4_blob_write $::b 10 HELLO} msg] - lappend rc $msg -} {1 SQLITE_READONLY} -do_test incrblob-7.5 { - sqlite4_errcode db -} {SQLITE_READONLY} -do_test incrblob-7.6 { - sqlite4_errmsg db -} {attempt to write a readonly database} - -# Test that if either the "offset" or "amount" arguments to -# sqlite4_blob_write() are less than zero, SQLITE_ERROR is returned. -# -do_test incrblob-8.1 { - execsql { INSERT INTO t1 VALUES(314159, 'sqlite') } - set ::b [db incrblob t1 b 314159] - fconfigure $::b -translation binary - set rc [catch {sqlite4_blob_write $::b 10 HELLO -1} msg] - lappend rc $msg -} {1 SQLITE_ERROR} -do_test incrblob-8.2 { - sqlite4_errcode db -} {SQLITE_ERROR} -do_test incrblob-8.3 { - set rc [catch {sqlite4_blob_write $::b -1 HELLO 5} msg] - lappend rc $msg -} {1 SQLITE_ERROR} -do_test incrblob-8.4 { - sqlite4_errcode db -} {SQLITE_ERROR} -do_test incrblob-8.5 { - execsql {SELECT b FROM t1 WHERE a = 314159} -} {sqlite} -do_test incrblob-8.6 { - set rc [catch {sqlite4_blob_write $::b 0 etilqs 6} msg] - lappend rc $msg -} {0 {}} -do_test incrblob-8.7 { - execsql {SELECT b FROM t1 WHERE a = 314159} -} {etilqs} - -# The following test case exposes an instance in the blob code where -# an error message was set using a call similar to sqlite4_mprintf(zErr), -# where zErr is an arbitrary string. This is no good if the string contains -# characters that can be mistaken for printf() formatting directives. -# -do_test incrblob-9.1 { - list [catch { db incrblob t1 "A tricky column name %s%s" 1 } msg] $msg -} {1 {no such column: "A tricky column name %s%s"}} - - -finish_test DELETED test/incrblob2.test Index: test/incrblob2.test ================================================================== --- test/incrblob2.test +++ /dev/null @@ -1,418 +0,0 @@ -# 2008 June 9 -# -# 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. -# -#*********************************************************************** -# -# Test that it is possible to have two open blob handles on a single -# blob object. -# -# $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $ -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -ifcapable {!autovacuum || !pragma || !incrblob} { - finish_test - return -} - -do_test incrblob2-1.0 { - execsql { - CREATE TABLE blobs(id INTEGER PRIMARY KEY, data BLOB); - INSERT INTO blobs VALUES(NULL, zeroblob(5000)); - INSERT INTO blobs VALUES(NULL, zeroblob(5000)); - INSERT INTO blobs VALUES(NULL, zeroblob(5000)); - INSERT INTO blobs VALUES(NULL, zeroblob(5000)); - } -} {} - -foreach iOffset [list 0 256 4094] { - do_test incrblob2-1.$iOffset.1 { - set fd [db incrblob blobs data 1] - puts $fd "[string repeat x $iOffset]SQLite version 3.6.0" - close $fd - } {} - - do_test incrblob2-1.$iOffset.2 { - set fd1 [db incrblob blobs data 1] - set fd2 [db incrblob blobs data 1] - fconfigure $fd1 -buffering none - fconfigure $fd2 -buffering none - if {$iOffset != 0} { - seek $fd2 $iOffset start - seek $fd1 $iOffset start - } - read $fd1 6 - } {SQLite} - - do_test incrblob2-1.$iOffset.3 { - read $fd2 6 - } {SQLite} - - do_test incrblob2-1.$iOffset.4 { - seek $fd2 $iOffset start - seek $fd1 $iOffset start - puts -nonewline $fd2 "etiLQS" - } {} - - - do_test incrblob2-1.$iOffset.5 { - seek $fd1 $iOffset start - read $fd1 6 - } {etiLQS} - - do_test incrblob2-1.$iOffset.6 { - seek $fd2 $iOffset start - read $fd2 6 - } {etiLQS} - - do_test incrblob2-1.$iOffset.7 { - seek $fd1 $iOffset start - read $fd1 6 - } {etiLQS} - - do_test incrblob2-1.$iOffset.8 { - close $fd1 - close $fd2 - } {} -} - -#-------------------------------------------------------------------------- - -foreach iOffset [list 0 256 4094] { - - do_test incrblob2-2.$iOffset.1 { - set fd1 [db incrblob blobs data 1] - seek $fd1 [expr $iOffset - 5000] end - fconfigure $fd1 -buffering none - - set fd2 [db incrblob blobs data 1] - seek $fd2 [expr $iOffset - 5000] end - fconfigure $fd2 -buffering none - - puts -nonewline $fd1 "123456" - } {} - - do_test incrblob2-2.$iOffset.2 { - read $fd2 6 - } {123456} - - do_test incrblob2-2.$iOffset.3 { - close $fd1 - close $fd2 - } {} -} - -do_test incrblob2-3.1 { - set fd1 [db incrblob blobs data 1] - fconfigure $fd1 -buffering none -} {} -do_test incrblob2-3.2 { - execsql { - INSERT INTO blobs VALUES(5, zeroblob(10240)); - } -} {} -do_test incrblob2-3.3 { - set rc [catch { read $fd1 6 } msg] - list $rc $msg -} {0 123456} -do_test incrblob2-3.4 { - close $fd1 -} {} - -#-------------------------------------------------------------------------- -# The following tests - incrblob2-4.* - test that blob handles are -# invalidated at the correct times. -# -do_test incrblob2-4.1 { - unset -nocomplain data - db eval BEGIN - db eval { CREATE TABLE t1(id INTEGER PRIMARY KEY, data BLOB); } - for {set ii 1} {$ii < 100} {incr ii} { - set data [string repeat "blob$ii" 500] - db eval { INSERT INTO t1 VALUES($ii, $data) } - } - db eval COMMIT -} {} - -proc aborted_handles {} { - global handles - - set aborted {} - for {set ii 1} {$ii < 100} {incr ii} { - set str "blob$ii" - set nByte [string length $str] - set iOffset [expr $nByte * $ii * 2] - - set rc [catch {sqlite4_blob_read $handles($ii) $iOffset $nByte} msg] - if {$rc && $msg eq "SQLITE_ABORT"} { - lappend aborted $ii - } else { - if {$rc || $msg ne $str} { - error "blob $ii: $msg" - } - } - } - set aborted -} - -do_test incrblob2-4.2 { - for {set ii 1} {$ii < 100} {incr ii} { - set handles($ii) [db incrblob t1 data $ii] - } - aborted_handles -} {} - -# Update row 3. This should abort handle 3 but leave all others untouched. -# -do_test incrblob2-4.3 { - db eval {UPDATE t1 SET data = data || '' WHERE id = 3} - aborted_handles -} {3} - -# Test that a write to handle 3 also returns SQLITE_ABORT. -# -do_test incrblob2-4.3.1 { - set rc [catch {sqlite4_blob_write $::handles(3) 10 HELLO} msg] - list $rc $msg -} {1 SQLITE_ABORT} - -# Delete row 14. This should abort handle 6 but leave all others untouched. -# -do_test incrblob2-4.4 { - db eval {DELETE FROM t1 WHERE id = 14} - aborted_handles -} {3 14} - -# Change the rowid of row 15 to 102. Should abort handle 15. -# -do_test incrblob2-4.5 { - db eval {UPDATE t1 SET id = 102 WHERE id = 15} - aborted_handles -} {3 14 15} - -# Clobber row 92 using INSERT OR REPLACE. -# -do_test incrblob2-4.6 { - db eval {INSERT OR REPLACE INTO t1 VALUES(92, zeroblob(1000))} - aborted_handles -} {3 14 15 92} - -# Clobber row 65 using UPDATE OR REPLACE on row 35. This should abort -# handles 35 and 65. -# -do_test incrblob2-4.7 { - db eval {UPDATE OR REPLACE t1 SET id = 65 WHERE id = 35} - aborted_handles -} {3 14 15 35 65 92} - -# Insert a couple of new rows. This should not invalidate any handles. -# -do_test incrblob2-4.9 { - db eval {INSERT INTO t1 SELECT NULL, data FROM t1} - aborted_handles -} {3 14 15 35 65 92} - -# Delete all rows from 1 to 25. This should abort all handles up to 25. -# -do_test incrblob2-4.9 { - db eval {DELETE FROM t1 WHERE id >=1 AND id <= 25} - aborted_handles -} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 35 65 92} - -# Delete the whole table (this will use sqlite4BtreeClearTable()). All handles -# should now be aborted. -# -do_test incrblob2-4.10 { - db eval {DELETE FROM t1} - aborted_handles -} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99} - -do_test incrblob2-4.1.X { - for {set ii 1} {$ii < 100} {incr ii} { - close $handles($ii) - } -} {} - -#-------------------------------------------------------------------------- -# The following tests - incrblob2-5.* - test that in shared cache an open -# blob handle counts as a read-lock on its table. -# -ifcapable shared_cache { - db close - set ::enable_shared_cache [sqlite4_enable_shared_cache 1] - - do_test incrblob2-5.1 { - sqlite4 db test.db - sqlite4 db2 test.db - - execsql { - INSERT INTO t1 VALUES(1, 'abcde'); - } - } {} - - do_test incrblob2-5.2 { - catchsql { INSERT INTO t1 VALUES(2, 'fghij') } db2 - } {0 {}} - - do_test incrblob2-5.3 { - set blob [db incrblob t1 data 1] - catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 - } {1 {database table is locked}} - - do_test incrblob2-5.4 { - close $blob - execsql BEGIN db2 - catchsql { INSERT INTO t1 VALUES(4, 'pqrst') } db2 - } {0 {}} - - do_test incrblob2-5.5 { - set rc [catch { db incrblob -readonly t1 data 1 } msg] - list $rc $msg - } {1 {database table is locked: t1}} - - do_test incrblob2-5.6 { - execsql { PRAGMA read_uncommitted=1 } - set blob [db incrblob -readonly t1 data 4] - read $blob - } {pqrst} - - do_test incrblob2-5.7 { - catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 - } {0 {}} - - do_test incrblob2-5.8 { - close $blob - } {} - - db2 close - db close - sqlite4_enable_shared_cache $::enable_shared_cache -} - -#-------------------------------------------------------------------------- -# The following tests - incrblob2-6.* - test a specific scenario that might -# be causing an error. -# -sqlite4 db test.db -do_test incrblob2-6.1 { - execsql { - DELETE FROM t1; - INSERT INTO t1 VALUES(1, zeroblob(100)); - } - - set rdHandle [db incrblob -readonly t1 data 1] - set wrHandle [db incrblob t1 data 1] - - sqlite4_blob_read $rdHandle 0 100 - - sqlite4_blob_write $wrHandle 0 ABCDEF - - close $wrHandle - close $rdHandle -} {} - -do_test incrblob2-6.2 { - set rdHandle [db incrblob -readonly t1 data 1] - sqlite4_blob_read $rdHandle 0 2 -} {AB} - -do_test incrblob2-6.3 { - set wrHandle [db incrblob t1 data 1] - sqlite4_blob_write $wrHandle 0 ZZZZZZZZZZ - sqlite4_blob_read $rdHandle 2 4 -} {ZZZZ} - -do_test incrblob2-6.4 { - close $wrHandle - close $rdHandle -} {} - -sqlite4_memory_highwater 1 -do_test incrblob2-7.1 { - db eval { - CREATE TABLE t2(B BLOB); - INSERT INTO t2 VALUES(zeroblob(10 * 1024 * 1024)); - } - expr {[sqlite4_memory_highwater]<(5 * 1024 * 1024)} -} {1} - -do_test incrblob2-7.2 { - set h [db incrblob t2 B 1] - expr {[sqlite4_memory_highwater]<(5 * 1024 * 1024)} -} {1} - -do_test incrblob2-7.3 { - seek $h 0 end - tell $h -} [expr 10 * 1024 * 1024] - -do_test incrblob2-7.4 { - expr {[sqlite4_memory_highwater]<(5 * 1024 * 1024)} -} {1} - -do_test incrblob2-7.5 { - close $h -} {} - -#--------------------------------------------------------------------------- -# The following tests, incrblob2-8.*, test that nothing terrible happens -# when a statement transaction is rolled back while there are open -# incremental-blob handles. At one point an assert() was failing when -# this was attempted. -# -do_test incrblob2-8.1 { - execsql BEGIN - set h [db incrblob t2 B 1] - set rc [catch { - db eval {SELECT rowid FROM t2} { execsql "DROP TABLE t2" } - } msg] - list $rc $msg -} {1 {database table is locked}} -do_test incrblob2-8.2 { - close $h - execsql COMMIT -} {} -do_test incrblob2-8.3 { - execsql { - CREATE TABLE t3(a INTEGER UNIQUE, b TEXT); - INSERT INTO t3 VALUES(1, 'aaaaaaaaaaaaaaaaaaaa'); - INSERT INTO t3 VALUES(2, 'bbbbbbbbbbbbbbbbbbbb'); - INSERT INTO t3 VALUES(3, 'cccccccccccccccccccc'); - INSERT INTO t3 VALUES(4, 'dddddddddddddddddddd'); - INSERT INTO t3 VALUES(5, 'eeeeeeeeeeeeeeeeeeee'); - } -} {} -do_test incrblob2-8.4 { - execsql BEGIN - set h [db incrblob t3 b 3] - sqlite4_blob_read $h 0 20 -} {cccccccccccccccccccc} -do_test incrblob2-8.5 { - catchsql {UPDATE t3 SET a = 6 WHERE a > 3} -} {1 {column a is not unique}} -do_test incrblob2-8.6 { - catchsql {UPDATE t3 SET a = 6 WHERE a > 3} -} {1 {column a is not unique}} -do_test incrblob2-8.7 { - sqlite4_blob_read $h 0 20 -} {cccccccccccccccccccc} -do_test incrblob2-8.8 { - catchsql {UPDATE t3 SET a = 6 WHERE a = 3 OR a = 5} -} {1 {column a is not unique}} -do_test incrblob2-8.9 { - set rc [catch {sqlite4_blob_read $h 0 20} msg] - list $rc $msg -} {1 SQLITE_ABORT} -do_test incrblob2-8.X { - close $h -} {} - -finish_test DELETED test/incrblob3.test Index: test/incrblob3.test ================================================================== --- test/incrblob3.test +++ /dev/null @@ -1,272 +0,0 @@ -# 2010 October 20 -# -# 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. -# -#*********************************************************************** -# -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -sqlite4 db test.db -sqlite4_db_config_lookaside db 0 0 0 - -do_execsql_test incrblob3-1.1 { - CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB); - INSERT INTO blobs VALUES(1, zeroblob(100)); - INSERT INTO blobs VALUES(2, zeroblob(100)); -} {} - -# Test the sqlite4_blob_reopen()/read()/write() functions. -# -do_test incrblob3-1.2 { - set ::blob [db incrblob blobs v 1] - puts $::blob "hello world" -} {} - -do_test incrblob3-1.3 { - sqlite4_blob_reopen $::blob 2 - puts $::blob "world hello" -} {} - -do_test incrblob3-1.4 { - sqlite4_blob_reopen $::blob 1 - gets $::blob -} {hello world} - -do_test incrblob3-1.5 { - sqlite4_blob_reopen $::blob 2 - gets $::blob -} {world hello} - -do_test incrblob3-1.6 { close $::blob } {} - -# Test some error conditions. -# -# incrblob3-2.1: Attempting to reopen a row that does not exist. -# incrblob3-2.2: Attempting to reopen a row that does not contain a blob -# or text value. -# -do_test incrblob3-2.1.1 { - set ::blob [db incrblob blobs v 1] - list [catch {sqlite4_blob_reopen $::blob 3} msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-2.1.2 { - list [sqlite4_errcode db] [sqlite4_errmsg db] -} {SQLITE_ERROR {no such rowid: 3}} -do_test incrblob3-2.1.3 { - list [catch {sqlite4_blob_reopen $::blob 1} msg] $msg -} {1 SQLITE_ABORT} -do_test incrblob3-2.1.4 { close $::blob } {} - -do_execsql_test incrblob3-2.2.1 { - INSERT INTO blobs VALUES(3, 42); - INSERT INTO blobs VALUES(4, 54.4); - INSERT INTO blobs VALUES(5, NULL); -} -foreach {tn rowid type} { - 1 3 integer - 2 4 real - 3 5 null -} { - do_test incrblob3-2.2.$tn.1 { - set ::blob [db incrblob blobs v 1] - list [catch {sqlite4_blob_reopen $::blob $rowid} msg] $msg - } {1 SQLITE_ERROR} - do_test incrblob3-2.2.$tn.2 { - list [sqlite4_errcode db] [sqlite4_errmsg db] - } "SQLITE_ERROR {cannot open value of type $type}" - - do_test incrblob3-2.2.$tn.3 { - list [catch {sqlite4_blob_reopen $::blob 1} msg] $msg - } {1 SQLITE_ABORT} - do_test incrblob3-2.2.$tn.4 { - list [catch {sqlite4_blob_read $::blob 0 10} msg] $msg - } {1 SQLITE_ABORT} - do_test incrblob3-2.2.$tn.5 { - list [catch {sqlite4_blob_write $::blob 0 "abcd"} msg] $msg - } {1 SQLITE_ABORT} - do_test incrblob3-2.2.$tn.6 { - sqlite4_blob_bytes $::blob - } {0} - - do_test incrblob3-2.2.$tn.7 { close $::blob } {} -} - -# Test that passing NULL to sqlite4_blob_XXX() APIs returns SQLITE_MISUSE. -# -# incrblob3-3.1: sqlite4_blob_reopen() -# incrblob3-3.2: sqlite4_blob_read() -# incrblob3-3.3: sqlite4_blob_write() -# incrblob3-3.4: sqlite4_blob_bytes() -# -do_test incrblob3-3.1 { - list [catch {sqlite4_blob_reopen {} 3} msg] $msg -} {1 SQLITE_MISUSE} - -do_test incrblob3-3.2 { - list [catch {sqlite4_blob_read {} 0 10} msg] $msg -} {1 SQLITE_MISUSE} - -do_test incrblob3-3.3 { - list [catch {sqlite4_blob_write {} 0 "abcd"} msg] $msg -} {1 SQLITE_MISUSE} - -do_test incrblob3-3.4 { sqlite4_blob_bytes {} } {0} - -do_test incrblob3-3.5 { sqlite4_blob_close {} } {} - -# Test out-of-range reading and writing -# -do_test incrblob3-4.1 { - set ::blob [db incrblob blobs v 1] - sqlite4_blob_bytes $::blob -} {100} -do_test incrblob3-4.2 { - list [catch { sqlite4_blob_read $::blob -1 10 } msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-4.3 { - list [catch { sqlite4_blob_read $::blob 0 -10 } msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-4.4 { - list [catch { sqlite4_blob_read $::blob 95 10 } msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-4.5 { - list [catch { sqlite4_blob_write $::blob -1 "abcdefghij" 10 } msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-4.6 { - list [catch { sqlite4_blob_write $::blob 0 "abcdefghij" -10 } msg] $msg -} {1 SQLITE_ERROR} -do_test incrblob3-4.7 { - list [catch { sqlite4_blob_write $::blob 95 "abcdefghij" } msg] $msg -} {1 SQLITE_ERROR} - -do_test incrblob3-4.8 { close $::blob } {} - -# Test that modifying the row a blob handle points to aborts the blob. -# -do_test incrblob3-5.1 { - set ::blob [db incrblob blobs v 1] - sqlite4_blob_bytes $::blob -} {100} -do_test incrblob3-5.2 { - execsql { UPDATE blobs SET v = '123456789012345678901234567890' WHERE k = 1 } - list [catch { sqlite4_blob_read $::blob 0 10 } msg] $msg -} {1 SQLITE_ABORT} - -# Test various errors that can occur in sqlite4_blob_open(): -# -# 1. Trying to open a virtual table column. -# 2. Trying to open a view column. -# 3. Trying to open a column that does not exist. -# 4. Trying to open a read/write handle on an indexed column. -# 5. Trying to open a read/write handle on the child key of an FK constraint. -# -ifcapable fts3 { - do_test incrblob3-6.1 { - execsql { - CREATE VIRTUAL TABLE ft USING fts3; - INSERT INTO ft VALUES('rules to open a column to which'); - } - - list [catch { db incrblob ft content 1 } msg] $msg - } {1 {cannot open virtual table: ft}} -} -ifcapable view { - do_test incrblob3-6.2 { - execsql { CREATE VIEW v1 AS SELECT * FROM blobs } - list [catch { db incrblob v1 content 1 } msg] $msg - } {1 {cannot open view: v1}} -} - -do_test incrblob3-6.3 { - list [catch { db incrblob blobs content 1 } msg] $msg -} {1 {no such column: "content"}} - -do_test incrblob3-6.4.1 { - execsql { - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(b); - INSERT INTO t1 VALUES(zeroblob(100), zeroblob(100)); - } - list [catch { db incrblob t1 b 1 } msg] $msg -} {1 {cannot open indexed column for writing}} -do_test incrblob3-6.4.2 { - set ::blob [db incrblob t1 a 1] - close $::blob -} {} -do_test incrblob3-6.4.3 { - set ::blob [db incrblob -readonly t1 b 1] - close $::blob -} {} - -do_test incrblob3-6.5.1 { - execsql { - CREATE TABLE p1(a PRIMARY KEY); - CREATE TABLE c1(a, b REFERENCES p1); - PRAGMA foreign_keys = 1; - INSERT INTO p1 VALUES(zeroblob(100)); - INSERT INTO c1 VALUES(zeroblob(100), zeroblob(100)); - } - list [catch { db incrblob c1 b 1 } msg] $msg -} {1 {cannot open foreign key column for writing}} - -do_test incrblob3-6.5.2 { - set ::blob [db incrblob c1 a 1] - close $::blob -} {} -do_test incrblob3-6.5.3 { - set ::blob [db incrblob -readonly c1 b 1] - close $::blob -} {} -do_test incrblob3-6.5.4 { - execsql { PRAGMA foreign_keys = 0 } - set ::blob [db incrblob c1 b 1] - close $::blob -} {} - - -# Test that sqlite4_blob_open() handles transient and persistent schema -# errors correctly. -# -do_test incrblob3-7.1 { - sqlite4 db2 test.db - sqlite4_db_config_lookaside db2 0 0 0 - execsql { CREATE TABLE t2(x) } db2 - set ::blob [db incrblob blobs v 1] - close $::blob -} {} -db2 close - -testvfs tvfs -default 1 -tvfs filter xAccess -tvfs script access_method - -proc access_method {args} { - set schemacookie [hexio_get_int [hexio_read test.db 40 4]] - incr schemacookie - hexio_write test.db 40 [hexio_render_int32 $schemacookie] - - set dbversion [hexio_get_int [hexio_read test.db 24 4]] - incr dbversion - hexio_write test.db 24 [hexio_render_int32 $dbversion] - - return "" -} - -do_test incrblob3-7.2 { - sqlite4 db test.db - sqlite4_db_config_lookaside db 0 0 0 - list [catch {db incrblob blobs v 1} msg] $msg -} {1 {database schema has changed}} -db close -tvfs delete - -finish_test - DELETED test/incrblob_err.test Index: test/incrblob_err.test ================================================================== --- test/incrblob_err.test +++ /dev/null @@ -1,139 +0,0 @@ -# 2007 May 1 -# -# 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. -# -#*********************************************************************** -# -# $Id: incrblob_err.test,v 1.14 2008/07/18 17:16:27 drh Exp $ -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -ifcapable {!incrblob || !memdebug || !tclvar} { - finish_test - return -} - -source $testdir/malloc_common.tcl - -unset -nocomplain ::fd ::data -set ::fd [open [info script]] -set ::data [read $::fd] -close $::fd - -do_malloc_test 1 -tclprep { - set bytes [file size [info script]] - execsql { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, zeroblob($::bytes)); - } -} -tclbody { - set ::blob [db incrblob blobs v 1] - fconfigure $::blob -translation binary - set rc [catch {puts -nonewline $::blob $::data}] - if {$rc} { error "out of memory" } -} - -do_malloc_test 2 -tclprep { - execsql { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, $::data); - } -} -tclbody { - set ::blob [db incrblob blobs v 1] - set rc [catch {set ::r [read $::blob]}] - if {$rc} { - error "out of memory" - } elseif {$::r ne $::data} { - error "Bad data read..." - } -} - -do_malloc_test 3 -tclprep { - execsql { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, $::data); - } -} -tclbody { - set ::blob [db incrblob blobs v 1] - set rc [catch {set ::r [read $::blob]}] - if {$rc} { - error "out of memory" - } elseif {$::r ne $::data} { - error "Bad data read..." - } - set rc [catch {close $::blob}] - if {$rc} { - error "out of memory" - } -} - -do_ioerr_test incrblob_err-4 -cksum 1 -sqlprep { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, $::data); -} -tclbody { - set ::blob [db incrblob blobs v 1] - read $::blob -} - -do_ioerr_test incrblob_err-5 -cksum 1 -sqlprep { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, zeroblob(length(CAST($::data AS BLOB)))); -} -tclbody { - set ::blob [db incrblob blobs v 1] - fconfigure $::blob -translation binary - puts -nonewline $::blob $::data - close $::blob -} - -do_ioerr_test incrblob_err-6 -cksum 1 -sqlprep { - CREATE TABLE blobs(k, v BLOB); - INSERT INTO blobs VALUES(1, $::data || $::data || $::data); -} -tclbody { - set ::blob [db incrblob blobs v 1] - fconfigure $::blob -translation binary - seek $::blob -20 end - puts -nonewline $::blob "12345678900987654321" - close $::blob -} - -do_ioerr_test incrblob_err-7 -cksum 1 -sqlprep { - PRAGMA auto_vacuum = 1; - CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB); - INSERT INTO blobs VALUES(1, zeroblob(500 * 1020)); -} -tclbody { - # Read some data from the end of the large blob inserted into table - # "blobs". This forces the IO error to occur while reading a pointer - # map page for the purposes of seeking to the end of the blob. - # - sqlite4 db2 test.db - set ::blob [db2 incrblob blobs v 1] - sqlite4_blob_read $::blob [expr 500*1020-20] 20 - close $::blob -} -catch {db2 close} - -do_ioerr_test incrblob_err-8 -cksum 1 -sqlprep { - PRAGMA auto_vacuum = 1; - CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB); - INSERT INTO blobs VALUES(1, zeroblob(500 * 1020)); -} -tclbody { - # Read some data from the end of the large blob inserted into table - # "blobs". This forces the IO error to occur while reading a pointer - # map page for the purposes of seeking to the end of the blob. - # - sqlite4 db2 test.db - set ::blob [db2 incrblob blobs v 1] - sqlite4_blob_write $::blob [expr 500*1020-20] 12345678900987654321 - close $::blob -} - -catch {db2 close} - -finish_test DELETED test/incrblobfault.test Index: test/incrblobfault.test ================================================================== --- test/incrblobfault.test +++ /dev/null @@ -1,70 +0,0 @@ -# 2010 October 26 -# -# 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. -# -#*********************************************************************** -# -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -set testprefix incrblobfault - -do_execsql_test 1.0 { - CREATE TABLE blob(x INTEGER PRIMARY KEY, v BLOB); - INSERT INTO blob VALUES(1, 'hello world'); - INSERT INTO blob VALUES(2, 'world hello'); - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; - INSERT INTO blob SELECT NULL, v FROM blob; -} - -do_faultsim_test 1 -prep { - sqlite4 db test.db - set ::blob [db incrblob blob v 1] -} -body { - if {[catch {sqlite4_blob_reopen $::blob 1000}]} { - error [sqlite4_errmsg db] - } -} -test { - faultsim_test_result {0 {}} - close $::blob -} - -do_faultsim_test 2 -prep { - sqlite4 db test.db - set ::blob [db incrblob blob v 1] -} -body { - if {[catch {sqlite4_blob_reopen $::blob -1}]} { - error [sqlite4_errmsg db] - } -} -test { - faultsim_test_result {1 {no such rowid: -1}} - close $::blob -} - -do_faultsim_test 3 -prep { - sqlite4 db test.db -} -body { - set ::blob [db incrblob blob v 1] - gets $::blob -} -test { - faultsim_test_result {0 {hello world}} - catch { close $::blob } -} - -finish_test -