mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Cause incremental-blob read/write operations lock shared-cache tables in the same way as normal SQL read/writes. Add complex assert statements to make sure tehe correct shared-cache locks are held when accessing the database. Eliminate some redundant checks from btree.c. (CVS 6830)
FossilOrigin-Name: f17ef37897da9bcaf20b5acdce6840522c0a0b16
This commit is contained in:
24
manifest
24
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Version\s3.6.16\s(CVS\s6829)
|
C Cause\sincremental-blob\sread/write\soperations\slock\sshared-cache\stables\sin\sthe\ssame\sway\sas\snormal\sSQL\sread/writes.\sAdd\scomplex\sassert\sstatements\sto\smake\ssure\stehe\scorrect\sshared-cache\slocks\sare\sheld\swhen\saccessing\sthe\sdatabase.\sEliminate\ssome\sredundant\schecks\sfrom\sbtree.c.\s(CVS\s6830)
|
||||||
D 2009-06-27T14:10:30
|
D 2009-06-29T06:00:37
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 8b8fb7823264331210cddf103831816c286ba446
|
F Makefile.in 8b8fb7823264331210cddf103831816c286ba446
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
@@ -106,7 +106,7 @@ F src/auth.c 98db07c2088455797678eb1031f42d4d94d18a71
|
|||||||
F src/backup.c ff50af53184a5fd7bdee4d620b5dabef74717c79
|
F src/backup.c ff50af53184a5fd7bdee4d620b5dabef74717c79
|
||||||
F src/bitvec.c 0ef0651714728055d43de7a4cdd95e703fac0119
|
F src/bitvec.c 0ef0651714728055d43de7a4cdd95e703fac0119
|
||||||
F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c
|
F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c
|
||||||
F src/btree.c 617d674eb77060f7fd6a05d27d72946c901191c8
|
F src/btree.c 078eb41016d033707f8a94075cdde18249e48f75
|
||||||
F src/btree.h f70b694e8c163227369a66863b01fbff9009f323
|
F src/btree.h f70b694e8c163227369a66863b01fbff9009f323
|
||||||
F src/btreeInt.h 55346bc14b939ad41b297942e8b1b581e960fb99
|
F src/btreeInt.h 55346bc14b939ad41b297942e8b1b581e960fb99
|
||||||
F src/build.c 813f6bdab5e4fb5ff94a5340c199a4930da9d66e
|
F src/build.c 813f6bdab5e4fb5ff94a5340c199a4930da9d66e
|
||||||
@@ -169,7 +169,7 @@ F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
|
|||||||
F src/tclsqlite.c d3195e0738c101a155404ecdb1cd9532a2fd34f2
|
F src/tclsqlite.c d3195e0738c101a155404ecdb1cd9532a2fd34f2
|
||||||
F src/test1.c c8f9358879876660b721369f576bf6e4ac5b9210
|
F src/test1.c c8f9358879876660b721369f576bf6e4ac5b9210
|
||||||
F src/test2.c d73e4a490349245fb196b990b80684513e0ceaee
|
F src/test2.c d73e4a490349245fb196b990b80684513e0ceaee
|
||||||
F src/test3.c abd651f387a42696976dd5560c46b596e421cfb8
|
F src/test3.c 1e1778ca7e0234c9eb329ac6a5729067b822f3d5
|
||||||
F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
|
F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
|
||||||
F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
|
F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
|
||||||
F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
|
F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
|
||||||
@@ -203,12 +203,12 @@ F src/update.c a1bbe774bce495d62dce3df3f42a5f04c1de173a
|
|||||||
F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff
|
F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff
|
||||||
F src/util.c 861d5b5c58be4921f0a254489ea94cb15f550ef8
|
F src/util.c 861d5b5c58be4921f0a254489ea94cb15f550ef8
|
||||||
F src/vacuum.c 0e14f371ea3326c6b8cfba257286d798cd20db59
|
F src/vacuum.c 0e14f371ea3326c6b8cfba257286d798cd20db59
|
||||||
F src/vdbe.c dfd508c9f6c183f0f39535dee51d9f0bc9420088
|
F src/vdbe.c f2462fdb71b8dfd788e2be1228b1b6c1b702ea80
|
||||||
F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a
|
F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a
|
||||||
F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007
|
F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007
|
||||||
F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624
|
F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624
|
||||||
F src/vdbeaux.c 3773217a73f93fb292d264b3b1da98c179a0f2f0
|
F src/vdbeaux.c 3773217a73f93fb292d264b3b1da98c179a0f2f0
|
||||||
F src/vdbeblob.c c25d7e7bc6d5917feeb17270bd275fa771f26e5c
|
F src/vdbeblob.c f3d3151d8ddfe9fed288f9e2c98c96e12e914fbb
|
||||||
F src/vdbemem.c 1618f685d19b4bcc96e40b3c478487bafd2ae246
|
F src/vdbemem.c 1618f685d19b4bcc96e40b3c478487bafd2ae246
|
||||||
F src/vtab.c 98fbffc5efe68d8107511dec0a650efc7daa9446
|
F src/vtab.c 98fbffc5efe68d8107511dec0a650efc7daa9446
|
||||||
F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
|
F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
|
||||||
@@ -397,7 +397,7 @@ F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
|||||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||||
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||||
F test/incrblob.test c80087a8ec28b4a58b5299251074048e17754f8f
|
F test/incrblob.test c80087a8ec28b4a58b5299251074048e17754f8f
|
||||||
F test/incrblob2.test 7ef4581745dd80155a451637aa779b49df90787d
|
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
|
||||||
F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
|
F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
|
||||||
F test/incrvacuum.test d0fb6ef6d747ef5c5ebe878aafa72dd3e178856b
|
F test/incrvacuum.test d0fb6ef6d747ef5c5ebe878aafa72dd3e178856b
|
||||||
F test/incrvacuum2.test 46ef65f377e3937cfd1ba66e818309dab46f590d
|
F test/incrvacuum2.test 46ef65f377e3937cfd1ba66e818309dab46f590d
|
||||||
@@ -672,7 +672,7 @@ F test/trigger8.test 83d92c212f36442d26527d6f7701575905a52ae1
|
|||||||
F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8
|
F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8
|
||||||
F test/triggerA.test 208dbda4d2f7c918b02f8a0dfa3acd2a0fe00691
|
F test/triggerA.test 208dbda4d2f7c918b02f8a0dfa3acd2a0fe00691
|
||||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||||
F test/types.test 98e7a631bddf0806204358b452b02d0e319318a6
|
F test/types.test 9a825ec8eea4e965d7113b74c76a78bb5240f2ac
|
||||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||||
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
|
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
|
||||||
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
||||||
@@ -737,7 +737,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
|||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
|
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
|
||||||
P 49f22e55d69d0b5a34400b36332a2eb861362eb2
|
P ff691a6b2a302fe7978459cb8df9d56184892ee0
|
||||||
R 081c6cf0e8f2499b8b69ecc027b9626f
|
R 78ed1dc8020260be15a9afc4441673c9
|
||||||
U drh
|
U danielk1977
|
||||||
Z b0d3fc590df87bc7076fef537dbc78a6
|
Z d5676c92a64d0bdadc5400d03f5280e3
|
||||||
|
@@ -1 +1 @@
|
|||||||
ff691a6b2a302fe7978459cb8df9d56184892ee0
|
f17ef37897da9bcaf20b5acdce6840522c0a0b16
|
286
src/btree.c
286
src/btree.c
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.645 2009/06/26 16:32:13 shane Exp $
|
** $Id: btree.c,v 1.646 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
**
|
**
|
||||||
** This file implements a external (disk-based) database using BTrees.
|
** This file implements a external (disk-based) database using BTrees.
|
||||||
** See the header comment on "btreeInt.h" for additional information.
|
** See the header comment on "btreeInt.h" for additional information.
|
||||||
@@ -67,11 +67,6 @@ int sqlite3_enable_shared_cache(int enable){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Forward declaration
|
|
||||||
*/
|
|
||||||
static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||||
/*
|
/*
|
||||||
@@ -86,9 +81,113 @@ static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
|
|||||||
#define querySharedCacheTableLock(a,b,c) SQLITE_OK
|
#define querySharedCacheTableLock(a,b,c) SQLITE_OK
|
||||||
#define setSharedCacheTableLock(a,b,c) SQLITE_OK
|
#define setSharedCacheTableLock(a,b,c) SQLITE_OK
|
||||||
#define clearAllSharedCacheTableLocks(a)
|
#define clearAllSharedCacheTableLocks(a)
|
||||||
|
#define hasSharedCacheTableLock(a,b,c,d) 1
|
||||||
|
#define hasReadConflicts(a, b) 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||||
|
|
||||||
|
#ifdef SQLITE_DEBUG
|
||||||
|
/*
|
||||||
|
** This function is only used as part of an assert() statement. It checks
|
||||||
|
** that connection p holds the required locks to read or write to the
|
||||||
|
** b-tree with root page iRoot. If so, true is returned. Otherwise, false.
|
||||||
|
** For example, when writing to a table b-tree with root-page iRoot via
|
||||||
|
** Btree connection pBtree:
|
||||||
|
**
|
||||||
|
** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) );
|
||||||
|
**
|
||||||
|
** When writing to an index b-tree that resides in a sharable database, the
|
||||||
|
** caller should have first obtained a lock specifying the root page of
|
||||||
|
** the corresponding table b-tree. This makes things a bit more complicated,
|
||||||
|
** as this module treats each b-tree as a separate structure. To determine
|
||||||
|
** the table b-tree corresponding to the index b-tree being written, this
|
||||||
|
** function has to search through the database schema.
|
||||||
|
**
|
||||||
|
** Instead of a lock on the b-tree 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 b-tree database is not shareable, or if the client is reading
|
||||||
|
** and has the read-uncommitted flag set, then no lock is required.
|
||||||
|
** In these cases return true immediately. If the client is reading
|
||||||
|
** or writing an index b-tree, but the schema is not loaded, then return
|
||||||
|
** true also. In this case the lock is required, but it is too difficult
|
||||||
|
** to check if the client actually holds it. This doesn't happen very
|
||||||
|
** often. */
|
||||||
|
if( (pBtree->sharable==0)
|
||||||
|
|| (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted))
|
||||||
|
|| (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==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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is also used as part of assert() statements only. It
|
||||||
|
** returns true if there exist one or more cursors open on the table
|
||||||
|
** with root page iRoot that do not belong to either connection pBtree
|
||||||
|
** or some other connection that has the read-uncommitted flag set.
|
||||||
|
**
|
||||||
|
** For example, before writing to page iRoot:
|
||||||
|
**
|
||||||
|
** 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
|
** 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
|
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
|
||||||
@@ -326,9 +425,41 @@ static void invalidateAllOverflowCache(BtShared *pBt){
|
|||||||
invalidateOverflowCache(p);
|
invalidateOverflowCache(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is called before modifying the contents of a table
|
||||||
|
** b-tree to invalidate any incrblob cursors that are open on the
|
||||||
|
** row or one of the rows being modified. Argument pgnoRoot is the
|
||||||
|
** root-page of the table b-tree.
|
||||||
|
**
|
||||||
|
** 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 this specific row.
|
||||||
|
*/
|
||||||
|
static void invalidateIncrblobCursors(
|
||||||
|
Btree *pBtree, /* The database file to check */
|
||||||
|
Pgno pgnoRoot, /* Look for read cursors on this btree */
|
||||||
|
i64 iRow, /* The rowid that might be changing */
|
||||||
|
int isClearTable /* True if all rows are being deleted */
|
||||||
|
){
|
||||||
|
BtCursor *p;
|
||||||
|
BtShared *pBt = pBtree->pBt;
|
||||||
|
assert( sqlite3BtreeHoldsMutex(pBtree) );
|
||||||
|
for(p=pBt->pCursor; p; p=p->pNext){
|
||||||
|
if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){
|
||||||
|
p->eState = CURSOR_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define invalidateOverflowCache(x)
|
#define invalidateOverflowCache(x)
|
||||||
#define invalidateAllOverflowCache(x)
|
#define invalidateAllOverflowCache(x)
|
||||||
|
#define invalidateIncrblobCursors(w,x,y,z)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3055,17 +3186,23 @@ static int btreeCursor(
|
|||||||
|
|
||||||
assert( sqlite3BtreeHoldsMutex(p) );
|
assert( sqlite3BtreeHoldsMutex(p) );
|
||||||
assert( wrFlag==0 || wrFlag==1 );
|
assert( wrFlag==0 || wrFlag==1 );
|
||||||
if( wrFlag ){
|
|
||||||
assert( !pBt->readOnly );
|
/* The following assert statements verify that if this is a sharable b-tree
|
||||||
if( NEVER(pBt->readOnly) ){
|
** database, the connection is holding the required table locks, and that
|
||||||
|
** no other connection has any open cursor that conflicts with this lock.
|
||||||
|
**
|
||||||
|
** The exception to this is read-only cursors open on the schema table.
|
||||||
|
** Such a cursor is opened without a lock while reading the database
|
||||||
|
** schema. This is safe because BtShared.mutex is held for the entire
|
||||||
|
** lifetime of this cursor. */
|
||||||
|
assert( (iTable==1 && wrFlag==0)
|
||||||
|
|| hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1)
|
||||||
|
);
|
||||||
|
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
|
||||||
|
|
||||||
|
if( NEVER(wrFlag && pBt->readOnly) ){
|
||||||
return SQLITE_READONLY;
|
return SQLITE_READONLY;
|
||||||
}
|
}
|
||||||
rc = checkForReadConflicts(p, iTable, 0, 0);
|
|
||||||
if( rc!=SQLITE_OK ){
|
|
||||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pBt->pPage1==0 ){
|
if( pBt->pPage1==0 ){
|
||||||
rc = lockBtreeWithRetry(p);
|
rc = lockBtreeWithRetry(p);
|
||||||
@@ -6210,75 +6347,6 @@ static int balance(BtCursor *pCur){
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** This routine checks all cursors that point to table pgnoRoot.
|
|
||||||
** If any of those cursors were opened with wrFlag==0 in a different
|
|
||||||
** database connection (a database connection that shares the pager
|
|
||||||
** cache with the current connection) and that other connection
|
|
||||||
** is not in the ReadUncommmitted state, then this routine returns
|
|
||||||
** SQLITE_LOCKED.
|
|
||||||
**
|
|
||||||
** As well as cursors with wrFlag==0, cursors with
|
|
||||||
** isIncrblobHandle==1 are also considered 'read' cursors because
|
|
||||||
** incremental blob cursors are used for both reading and writing.
|
|
||||||
**
|
|
||||||
** When pgnoRoot is the root page of an intkey table, this function is also
|
|
||||||
** responsible for invalidating incremental blob cursors when the table row
|
|
||||||
** on which they are opened is deleted or modified. Cursors are invalidated
|
|
||||||
** according to the following rules:
|
|
||||||
**
|
|
||||||
** 1) When BtreeClearTable() is called to completely delete the contents
|
|
||||||
** of a B-Tree table, pExclude is set to zero and parameter iRow is
|
|
||||||
** set to non-zero. In this case all incremental blob cursors open
|
|
||||||
** on the table rooted at pgnoRoot are invalidated.
|
|
||||||
**
|
|
||||||
** 2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to
|
|
||||||
** modify a table row via an SQL statement, pExclude is set to the
|
|
||||||
** write cursor used to do the modification and parameter iRow is set
|
|
||||||
** to the integer row id of the B-Tree entry being modified. Unless
|
|
||||||
** pExclude is itself an incremental blob cursor, then all incremental
|
|
||||||
** blob cursors open on row iRow of the B-Tree are invalidated.
|
|
||||||
**
|
|
||||||
** 3) If both pExclude and iRow are set to zero, no incremental blob
|
|
||||||
** cursors are invalidated.
|
|
||||||
*/
|
|
||||||
static int checkForReadConflicts(
|
|
||||||
Btree *pBtree, /* The database file to check */
|
|
||||||
Pgno pgnoRoot, /* Look for read cursors on this btree */
|
|
||||||
BtCursor *pExclude, /* Ignore this cursor */
|
|
||||||
i64 iRow /* The rowid that might be changing */
|
|
||||||
){
|
|
||||||
BtCursor *p;
|
|
||||||
BtShared *pBt = pBtree->pBt;
|
|
||||||
sqlite3 *db = pBtree->db;
|
|
||||||
assert( sqlite3BtreeHoldsMutex(pBtree) );
|
|
||||||
for(p=pBt->pCursor; p; p=p->pNext){
|
|
||||||
if( p==pExclude ) continue;
|
|
||||||
if( p->pgnoRoot!=pgnoRoot ) continue;
|
|
||||||
#ifndef SQLITE_OMIT_INCRBLOB
|
|
||||||
if( p->isIncrblobHandle && (
|
|
||||||
(!pExclude && iRow)
|
|
||||||
|| (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow)
|
|
||||||
)){
|
|
||||||
p->eState = CURSOR_INVALID;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if( p->eState!=CURSOR_VALID ) continue;
|
|
||||||
if( p->wrFlag==0
|
|
||||||
#ifndef SQLITE_OMIT_INCRBLOB
|
|
||||||
|| p->isIncrblobHandle
|
|
||||||
#endif
|
|
||||||
){
|
|
||||||
sqlite3 *dbOther = p->pBtree->db;
|
|
||||||
assert(dbOther);
|
|
||||||
if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){
|
|
||||||
sqlite3ConnectionBlocked(db, dbOther);
|
|
||||||
return SQLITE_LOCKED_SHAREDCACHE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Insert a new record into the BTree. The key is given by (pKey,nKey)
|
** Insert a new record into the BTree. The key is given by (pKey,nKey)
|
||||||
@@ -6322,12 +6390,15 @@ int sqlite3BtreeInsert(
|
|||||||
assert( pBt->inTransaction==TRANS_WRITE );
|
assert( pBt->inTransaction==TRANS_WRITE );
|
||||||
assert( !pBt->readOnly );
|
assert( !pBt->readOnly );
|
||||||
assert( pCur->wrFlag );
|
assert( pCur->wrFlag );
|
||||||
rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey);
|
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
|
||||||
if( rc ){
|
|
||||||
/* The table pCur points to has a read lock */
|
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
** cursors open on the row being replaced (assuming this is a replace
|
||||||
return rc;
|
** operation - if it is not, the following is a no-op). */
|
||||||
|
if( pCur->pKeyInfo==0 ){
|
||||||
|
invalidateIncrblobCursors(p, pCur->pgnoRoot, nKey, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( pCur->eState==CURSOR_FAULT ){
|
if( pCur->eState==CURSOR_FAULT ){
|
||||||
return pCur->skip;
|
return pCur->skip;
|
||||||
}
|
}
|
||||||
@@ -6448,16 +6519,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
assert( pBt->inTransaction==TRANS_WRITE );
|
assert( pBt->inTransaction==TRANS_WRITE );
|
||||||
assert( !pBt->readOnly );
|
assert( !pBt->readOnly );
|
||||||
assert( pCur->wrFlag );
|
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)
|
if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell)
|
||||||
|| NEVER(pCur->eState!=CURSOR_VALID)
|
|| NEVER(pCur->eState!=CURSOR_VALID)
|
||||||
){
|
){
|
||||||
return SQLITE_ERROR; /* Something has gone awry. */
|
return SQLITE_ERROR; /* Something has gone awry. */
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey);
|
/* If this is a delete operation to remove a row from a table b-tree,
|
||||||
if( rc!=SQLITE_OK ){
|
** invalidate any incrblob cursors open on the row being deleted. */
|
||||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
if( pCur->pKeyInfo==0 ){
|
||||||
return rc; /* The table pCur points to has a read lock */
|
invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
iCellDepth = pCur->iPage;
|
iCellDepth = pCur->iPage;
|
||||||
@@ -6757,11 +6831,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
|||||||
BtShared *pBt = p->pBt;
|
BtShared *pBt = p->pBt;
|
||||||
sqlite3BtreeEnter(p);
|
sqlite3BtreeEnter(p);
|
||||||
assert( p->inTrans==TRANS_WRITE );
|
assert( p->inTrans==TRANS_WRITE );
|
||||||
if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){
|
|
||||||
/* nothing to do */
|
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
|
||||||
}else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
|
** is the root of a table b-tree - if it is not, the following call is
|
||||||
/* nothing to do */
|
** a no-op). */
|
||||||
}else{
|
invalidateIncrblobCursors(p, iTable, 0, 1);
|
||||||
|
|
||||||
|
if( SQLITE_OK==(rc = saveAllCursors(pBt, (Pgno)iTable, 0)) ){
|
||||||
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
|
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
|
||||||
}
|
}
|
||||||
sqlite3BtreeLeave(p);
|
sqlite3BtreeLeave(p);
|
||||||
@@ -7687,8 +7763,6 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
|
|||||||
** to change the length of the data stored.
|
** to change the length of the data stored.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
||||||
int rc;
|
|
||||||
|
|
||||||
assert( cursorHoldsMutex(pCsr) );
|
assert( cursorHoldsMutex(pCsr) );
|
||||||
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
|
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
|
||||||
assert( pCsr->isIncrblobHandle );
|
assert( pCsr->isIncrblobHandle );
|
||||||
@@ -7707,14 +7781,10 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
|||||||
if( !pCsr->wrFlag ){
|
if( !pCsr->wrFlag ){
|
||||||
return SQLITE_READONLY;
|
return SQLITE_READONLY;
|
||||||
}
|
}
|
||||||
assert( !pCsr->pBt->readOnly
|
assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
|
||||||
&& pCsr->pBt->inTransaction==TRANS_WRITE );
|
assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
|
||||||
rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0);
|
assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
|
||||||
if( rc!=SQLITE_OK ){
|
|
||||||
/* The table pCur points to has a read lock */
|
|
||||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
|
if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
|
||||||
return SQLITE_ERROR;
|
return SQLITE_ERROR;
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
** is not included in the SQLite library. It is used for automated
|
** is not included in the SQLite library. It is used for automated
|
||||||
** testing of the SQLite library.
|
** testing of the SQLite library.
|
||||||
**
|
**
|
||||||
** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $
|
** $Id: test3.c,v 1.105 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "btreeInt.h"
|
#include "btreeInt.h"
|
||||||
@@ -626,7 +626,10 @@ static int btree_cursor(
|
|||||||
pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
|
pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
|
||||||
memset(pCur, 0, sqlite3BtreeCursorSize());
|
memset(pCur, 0, sqlite3BtreeCursorSize());
|
||||||
sqlite3BtreeEnter(pBt);
|
sqlite3BtreeEnter(pBt);
|
||||||
|
rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
|
||||||
|
if( rc==SQLITE_OK ){
|
||||||
rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
|
rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
|
||||||
|
}
|
||||||
sqlite3BtreeLeave(pBt);
|
sqlite3BtreeLeave(pBt);
|
||||||
if( rc ){
|
if( rc ){
|
||||||
ckfree((char *)pCur);
|
ckfree((char *)pCur);
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
** in this file for details. If in doubt, do not deviate from existing
|
** in this file for details. If in doubt, do not deviate from existing
|
||||||
** commenting and indentation practices when changing or adding code.
|
** commenting and indentation practices when changing or adding code.
|
||||||
**
|
**
|
||||||
** $Id: vdbe.c,v 1.866 2009/06/26 16:32:13 shane Exp $
|
** $Id: vdbe.c,v 1.867 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "vdbeInt.h"
|
#include "vdbeInt.h"
|
||||||
@@ -4994,7 +4994,7 @@ case OP_Expire: {
|
|||||||
** Obtain a lock on a particular table. This instruction is only used when
|
** Obtain a lock on a particular table. This instruction is only used when
|
||||||
** the shared-cache feature is enabled.
|
** the shared-cache feature is enabled.
|
||||||
**
|
**
|
||||||
** If P1 is the index of the database in sqlite3.aDb[] of the database
|
** P1 is the index of the database in sqlite3.aDb[] of the database
|
||||||
** on which the lock is acquired. A readlock is obtained if P3==0 or
|
** on which the lock is acquired. A readlock is obtained if P3==0 or
|
||||||
** a write lock if P3==1.
|
** a write lock if P3==1.
|
||||||
**
|
**
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
**
|
**
|
||||||
** This file contains code used to implement incremental BLOB I/O.
|
** This file contains code used to implement incremental BLOB I/O.
|
||||||
**
|
**
|
||||||
** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $
|
** $Id: vdbeblob.c,v 1.34 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
@@ -66,19 +66,18 @@ int sqlite3_blob_open(
|
|||||||
static const VdbeOpList openBlob[] = {
|
static const VdbeOpList openBlob[] = {
|
||||||
{OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
|
{OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
|
||||||
{OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
|
{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
|
/* One of the following two instructions is replaced by an OP_Noop. */
|
||||||
** OP_Noop before exection.
|
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
|
||||||
*/
|
{OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
|
||||||
{OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
|
|
||||||
{OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
|
|
||||||
|
|
||||||
{OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
|
{OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
|
||||||
{OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */
|
{OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */
|
||||||
{OP_Column, 0, 0, 1}, /* 6 */
|
{OP_Column, 0, 0, 1}, /* 7 */
|
||||||
{OP_ResultRow, 1, 0, 0}, /* 7 */
|
{OP_ResultRow, 1, 0, 0}, /* 8 */
|
||||||
{OP_Close, 0, 0, 0}, /* 8 */
|
{OP_Close, 0, 0, 0}, /* 9 */
|
||||||
{OP_Halt, 0, 0, 0}, /* 9 */
|
{OP_Halt, 0, 0, 0}, /* 10 */
|
||||||
};
|
};
|
||||||
|
|
||||||
Vdbe *v = 0;
|
Vdbe *v = 0;
|
||||||
@@ -170,10 +169,11 @@ int sqlite3_blob_open(
|
|||||||
if( v ){
|
if( v ){
|
||||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||||
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
|
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
|
||||||
|
flags = !!flags; /* flags = (flags ? 1 : 0); */
|
||||||
|
|
||||||
/* Configure the OP_Transaction */
|
/* Configure the OP_Transaction */
|
||||||
sqlite3VdbeChangeP1(v, 0, iDb);
|
sqlite3VdbeChangeP1(v, 0, iDb);
|
||||||
sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0));
|
sqlite3VdbeChangeP2(v, 0, flags);
|
||||||
|
|
||||||
/* Configure the OP_VerifyCookie */
|
/* Configure the OP_VerifyCookie */
|
||||||
sqlite3VdbeChangeP1(v, 1, iDb);
|
sqlite3VdbeChangeP1(v, 1, iDb);
|
||||||
@@ -182,13 +182,17 @@ int sqlite3_blob_open(
|
|||||||
/* Make sure a mutex is held on the table to be accessed */
|
/* Make sure a mutex is held on the table to be accessed */
|
||||||
sqlite3VdbeUsesBtree(v, iDb);
|
sqlite3VdbeUsesBtree(v, iDb);
|
||||||
|
|
||||||
|
/* Configure the OP_TableLock instruction */
|
||||||
|
sqlite3VdbeChangeP1(v, 2, iDb);
|
||||||
|
sqlite3VdbeChangeP2(v, 2, pTab->tnum);
|
||||||
|
sqlite3VdbeChangeP3(v, 2, flags);
|
||||||
|
sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
|
||||||
|
|
||||||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||||
** parameter of the other to pTab->tnum.
|
** parameter of the other to pTab->tnum. */
|
||||||
*/
|
sqlite3VdbeChangeToNoop(v, 4 - flags, 1);
|
||||||
flags = !!flags;
|
sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
|
||||||
sqlite3VdbeChangeToNoop(v, 3 - flags, 1);
|
sqlite3VdbeChangeP3(v, 3 + flags, iDb);
|
||||||
sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
|
|
||||||
sqlite3VdbeChangeP3(v, 2 + flags, iDb);
|
|
||||||
|
|
||||||
/* Configure the number of columns. Configure the cursor to
|
/* Configure the number of columns. Configure the cursor to
|
||||||
** think that the table has one more column than it really
|
** think that the table has one more column than it really
|
||||||
@@ -197,8 +201,8 @@ int sqlite3_blob_open(
|
|||||||
** we can invoke OP_Column to fill in the vdbe cursors type
|
** we can invoke OP_Column to fill in the vdbe cursors type
|
||||||
** and offset cache without causing any IO.
|
** and offset cache without causing any IO.
|
||||||
*/
|
*/
|
||||||
sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
|
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
|
||||||
sqlite3VdbeChangeP2(v, 6, pTab->nCol);
|
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
|
||||||
if( !db->mallocFailed ){
|
if( !db->mallocFailed ){
|
||||||
sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
|
sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
# Test that it is possible to have two open blob handles on a single
|
# Test that it is possible to have two open blob handles on a single
|
||||||
# blob object.
|
# blob object.
|
||||||
#
|
#
|
||||||
# $Id: incrblob2.test,v 1.10 2009/03/16 13:19:36 danielk1977 Exp $
|
# $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
#
|
#
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
@@ -274,15 +274,24 @@ ifcapable shared_cache {
|
|||||||
} {0 {}}
|
} {0 {}}
|
||||||
|
|
||||||
do_test incrblob2-5.5 {
|
do_test incrblob2-5.5 {
|
||||||
set blob [db incrblob -readonly t1 data 1]
|
set rc [catch { db incrblob -readonly t1 data 1 } msg]
|
||||||
catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2
|
list $rc $msg
|
||||||
} {1 {database table is locked}}
|
} {1 {database table is locked: t1}}
|
||||||
|
|
||||||
do_test incrblob2-5.6 {
|
do_test incrblob2-5.6 {
|
||||||
close $blob
|
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
|
catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2
|
||||||
} {0 {}}
|
} {0 {}}
|
||||||
|
|
||||||
|
do_test incrblob2-5.8 {
|
||||||
|
close $blob
|
||||||
|
} {}
|
||||||
|
|
||||||
db2 close
|
db2 close
|
||||||
db close
|
db close
|
||||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
# it tests that the different storage classes (integer, real, text etc.)
|
# it tests that the different storage classes (integer, real, text etc.)
|
||||||
# all work correctly.
|
# all work correctly.
|
||||||
#
|
#
|
||||||
# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $
|
# $Id: types.test,v 1.20 2009/06/29 06:00:37 danielk1977 Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -136,6 +136,7 @@ execsql {
|
|||||||
# in the table, in the tables default scanning order.
|
# in the table, in the tables default scanning order.
|
||||||
proc record_sizes {rootpage} {
|
proc record_sizes {rootpage} {
|
||||||
set bt [btree_open test.db 10 0]
|
set bt [btree_open test.db 10 0]
|
||||||
|
btree_begin_transaction $bt
|
||||||
set c [btree_cursor $bt $rootpage 0]
|
set c [btree_cursor $bt $rootpage 0]
|
||||||
btree_first $c
|
btree_first $c
|
||||||
while 1 {
|
while 1 {
|
||||||
|
Reference in New Issue
Block a user