SQLite

Check-in [f17ef37897]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f17ef37897da9bcaf20b5acdce6840522c0a0b16
User & Date: danielk1977 2009-06-29 06:00:37.000
Context
2009-07-01
14:56
Do not ignore alias "a" in a query of the form "SELECT ... FROM (...) AS a" Fix for #3935. Also expand upon (6751) to fix some similar obscure memory leaks. (CVS 6831) (check-in: 42f9d1e564 user: danielk1977 tags: trunk)
2009-06-29
06: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) (check-in: f17ef37897 user: danielk1977 tags: trunk)
2009-06-27
14:10
Version 3.6.16 (CVS 6829) (check-in: ff691a6b2a user: drh tags: trunk, release)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.645 2009/06/26 16:32:13 shane Exp $
**
** 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"












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.646 2009/06/29 06:00:37 danielk1977 Exp $
**
** 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"

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
int sqlite3_enable_shared_cache(int enable){
  sqlite3GlobalConfig.sharedCacheEnabled = enable;
  return SQLITE_OK;
}
#endif


/*
** Forward declaration
*/
static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);


#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)


#endif

#ifndef SQLITE_OMIT_SHARED_CACHE






































































































/*
** 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){







<
<
<
<
<














>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
int sqlite3_enable_shared_cache(int enable){
  sqlite3GlobalConfig.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 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. 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 
** (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){
322
323
324
325
326
327
328































329
330
331

332
333
334
335
336
337
338
static void invalidateAllOverflowCache(BtShared *pBt){
  BtCursor *p;
  assert( sqlite3_mutex_held(pBt->mutex) );
  for(p=pBt->pCursor; p; p=p->pNext){
    invalidateOverflowCache(p);
  }
}































#else
  #define invalidateOverflowCache(x)
  #define invalidateAllOverflowCache(x)

#endif

/*
** Set bit pgno of the BtShared.pHasContent bitvec. This is called 
** when a page that previously contained data becomes a free-list leaf 
** page.
**







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
static void invalidateAllOverflowCache(BtShared *pBt){
  BtCursor *p;
  assert( sqlite3_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
** 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
  #define invalidateOverflowCache(x)
  #define invalidateAllOverflowCache(x)
  #define invalidateIncrblobCursors(w,x,y,z)
#endif

/*
** Set bit pgno of the BtShared.pHasContent bitvec. This is called 
** when a page that previously contained data becomes a free-list leaf 
** page.
**
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062











3063
3064
3065
3066
3067


3068
3069
3070
3071
3072
3073
3074
){
  int rc;
  Pgno nPage;
  BtShared *pBt = p->pBt;

  assert( sqlite3BtreeHoldsMutex(p) );
  assert( wrFlag==0 || wrFlag==1 );
  if( wrFlag ){
    assert( !pBt->readOnly );
    if( NEVER(pBt->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 ){
    rc = lockBtreeWithRetry(p);
    if( rc!=SQLITE_OK ){
      return rc;
    }







<
<
<
<
|
>
>
>
>
>
>
>
>
>
>
>
|
<
<
<
|
>
>







3182
3183
3184
3185
3186
3187
3188




3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201



3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
){
  int rc;
  Pgno nPage;
  BtShared *pBt = p->pBt;

  assert( sqlite3BtreeHoldsMutex(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.
  **
  ** 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;
  }

  if( pBt->pPage1==0 ){
    rc = lockBtreeWithRetry(p);
    if( rc!=SQLITE_OK ){
      return rc;
    }
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288

  if( pFree ){
    sqlite3PageFree(pFree);
  }
  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)
** 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.
**







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







6343
6344
6345
6346
6347
6348
6349





































































6350
6351
6352
6353
6354
6355
6356

  if( pFree ){
    sqlite3PageFree(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.
**
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328

6329

6330

6331
6332
6333
6334
6335
6336
6337
  unsigned char *oldCell;
  unsigned char *newCell = 0;

  assert( cursorHoldsMutex(pCur) );
  assert( pBt->inTransaction==TRANS_WRITE );
  assert( !pBt->readOnly );
  assert( pCur->wrFlag );
  rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey);
  if( rc ){             
    /* The table pCur points to has a read lock */
    assert( rc==SQLITE_LOCKED_SHAREDCACHE );

    return rc;

  }

  if( pCur->eState==CURSOR_FAULT ){
    return pCur->skip;
  }

  /* Save the positions of any other cursors open on this table.
  **
  ** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For







|
|
|
|
>
|
>

>







6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
  unsigned char *oldCell;
  unsigned char *newCell = 0;

  assert( cursorHoldsMutex(pCur) );
  assert( pBt->inTransaction==TRANS_WRITE );
  assert( !pBt->readOnly );
  assert( pCur->wrFlag );
  assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );

  /* 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, pCur->pgnoRoot, nKey, 0);
  }

  if( pCur->eState==CURSOR_FAULT ){
    return pCur->skip;
  }

  /* Save the positions of any other cursors open on this table.
  **
  ** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For
6444
6445
6446
6447
6448
6449
6450



6451
6452
6453
6454
6455
6456



6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
  int iCellIdx;                        /* Index of cell to delete */
  int iCellDepth;                      /* Depth of node containing pCell */ 

  assert( cursorHoldsMutex(pCur) );
  assert( pBt->inTransaction==TRANS_WRITE );
  assert( !pBt->readOnly );
  assert( pCur->wrFlag );



  if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) 
   || NEVER(pCur->eState!=CURSOR_VALID)
  ){
    return SQLITE_ERROR;  /* Something has gone awry. */
  }




  rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey);
  if( rc!=SQLITE_OK ){
    assert( rc==SQLITE_LOCKED_SHAREDCACHE );
    return rc;            /* The table pCur points to has a read lock */
  }

  iCellDepth = pCur->iPage;
  iCellIdx = pCur->aiIdx[iCellDepth];
  pPage = pCur->apPage[iCellDepth];
  pCell = findCell(pPage, iCellIdx);








>
>
>






>
>
>
|
<
<
<







6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534



6535
6536
6537
6538
6539
6540
6541
  int iCellIdx;                        /* Index of cell to delete */
  int iCellDepth;                      /* Depth of node containing pCell */ 

  assert( cursorHoldsMutex(pCur) );
  assert( pBt->inTransaction==TRANS_WRITE );
  assert( !pBt->readOnly );
  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->pgnoRoot, pCur->info.nKey, 0);



  }

  iCellDepth = pCur->iPage;
  iCellIdx = pCur->aiIdx[iCellDepth];
  pPage = pCur->apPage[iCellDepth];
  pCell = findCell(pPage, iCellIdx);

6753
6754
6755
6756
6757
6758
6759
6760




6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
** entries in the table.
*/
int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  assert( p->inTrans==TRANS_WRITE );
  if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){




    /* nothing to do */
  }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
    /* nothing to do */
  }else{
    rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*







|
>
>
>
>
|
|
<
<







6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840


6841
6842
6843
6844
6845
6846
6847
** entries in the table.
*/
int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(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, iTable, 0, 1);

  if( SQLITE_OK==(rc = saveAllCursors(pBt, (Pgno)iTable, 0)) ){


    rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
** 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.
*/
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
  int rc;

  assert( cursorHoldsMutex(pCsr) );
  assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
  assert(pCsr->isIncrblobHandle);

  restoreCursorPosition(pCsr);
  assert( pCsr->eState!=CURSOR_REQUIRESEEK );
  if( pCsr->eState!=CURSOR_VALID ){
    return SQLITE_ABORT;
  }

  /* Check some preconditions: 
  **   (a) the cursor is open for writing,
  **   (b) there is no read-lock on the table being modified and
  **   (c) the cursor points at a valid row of an intKey table.
  */
  if( !pCsr->wrFlag ){
    return SQLITE_READONLY;
  }
  assert( !pCsr->pBt->readOnly 
          && pCsr->pBt->inTransaction==TRANS_WRITE );
  rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0);
  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 ){
    return SQLITE_ERROR;
  }

  return accessPayload(pCsr, offset, amt, (unsigned char *)z, 0, 1);
}








<
<


|















|
|
|
<
<
<
<
|







7759
7760
7761
7762
7763
7764
7765


7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786




7787
7788
7789
7790
7791
7792
7793
7794
** 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.
*/
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){


  assert( cursorHoldsMutex(pCsr) );
  assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
  assert( pCsr->isIncrblobHandle );

  restoreCursorPosition(pCsr);
  assert( pCsr->eState!=CURSOR_REQUIRESEEK );
  if( pCsr->eState!=CURSOR_VALID ){
    return SQLITE_ABORT;
  }

  /* Check some preconditions: 
  **   (a) the cursor is open for writing,
  **   (b) there is no read-lock on the table being modified and
  **   (c) the cursor points at a valid row of an intKey table.
  */
  if( !pCsr->wrFlag ){
    return SQLITE_READONLY;
  }
  assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
  assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
  assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );





  if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
    return SQLITE_ERROR;
  }

  return accessPayload(pCsr, offset, amt, (unsigned char *)z, 0, 1);
}

Changes to src/test3.c.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    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.
**
** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>








|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    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.
**
** $Id: test3.c,v 1.105 2009/06/29 06:00:37 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

622
623
624
625
626
627
628


629

630
631
632
633
634
635
636
  }
  pBt = sqlite3TestTextToPtr(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(sqlite3BtreeCursorSize());
  memset(pCur, 0, sqlite3BtreeCursorSize());
  sqlite3BtreeEnter(pBt);


  rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);

  sqlite3BtreeLeave(pBt);
  if( rc ){
    ckfree((char *)pCur);
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);







>
>
|
>







622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  }
  pBt = sqlite3TestTextToPtr(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(sqlite3BtreeCursorSize());
  memset(pCur, 0, sqlite3BtreeCursorSize());
  sqlite3BtreeEnter(pBt);
  rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
  if( rc==SQLITE_OK ){
    rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
  }
  sqlite3BtreeLeave(pBt);
  if( rc ){
    ckfree((char *)pCur);
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
Changes to src/vdbe.c.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.866 2009/06/26 16:32:13 shane Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes.  The test







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.867 2009/06/29 06:00:37 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes.  The test
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004

#ifndef SQLITE_OMIT_SHARED_CACHE
/* Opcode: TableLock P1 P2 P3 P4 *
**
** Obtain a lock on a particular table. This instruction is only used when
** the shared-cache feature is enabled. 
**
** If 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
** a write lock if P3==1.
**
** P2 contains the root-page of the table to lock.
**
** 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.







|







4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004

#ifndef SQLITE_OMIT_SHARED_CACHE
/* Opcode: TableLock P1 P2 P3 P4 *
**
** Obtain a lock on a particular table. This instruction is only used when
** the shared-cache feature is enabled. 
**
** 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
** a write lock if P3==1.
**
** P2 contains the root-page of the table to lock.
**
** 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.
Changes to src/vdbeblob.c.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
**    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.
**
** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB








|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
**    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.
**
** $Id: vdbeblob.c,v 1.34 2009/06/29 06:00:37 danielk1977 Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

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
  ** The sqlite3_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 */


    /* One of the following two instructions is replaced by an
    ** OP_Noop before exection.
    */
    {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_NotExists, 0, 8, 1},       /* 5: Seek the cursor */
    {OP_Column, 0, 0, 1},          /* 6  */
    {OP_ResultRow, 1, 0, 0},       /* 7  */
    {OP_Close, 0, 0, 0},           /* 8  */
    {OP_Halt, 0, 0, 0},            /* 9 */
  };

  Vdbe *v = 0;
  int rc = SQLITE_OK;
  char *zErr = 0;
  Table *pTab;
  Parse *pParse;







>

|
<
<
|
|

|
|
|
|
|
|







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
  ** The sqlite3_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, 9, 1},       /* 6: Seek the cursor */
    {OP_Column, 0, 0, 1},          /* 7  */
    {OP_ResultRow, 1, 0, 0},       /* 8  */
    {OP_Close, 0, 0, 0},           /* 9  */
    {OP_Halt, 0, 0, 0},            /* 10 */
  };

  Vdbe *v = 0;
  int rc = SQLITE_OK;
  char *zErr = 0;
  Table *pTab;
  Parse *pParse;
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180
181
182
183
184






185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
      }
    }

    v = sqlite3VdbeCreate(db);
    if( v ){
      int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
      sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);


      /* Configure the OP_Transaction */
      sqlite3VdbeChangeP1(v, 0, iDb);
      sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0));

      /* Configure the OP_VerifyCookie */
      sqlite3VdbeChangeP1(v, 1, iDb);
      sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);

      /* Make sure a mutex is held on the table to be accessed */
      sqlite3VdbeUsesBtree(v, iDb); 







      /* Remove either the OP_OpenWrite or OpenRead. Set the P2 
      ** parameter of the other to pTab->tnum. 
      */
      flags = !!flags;
      sqlite3VdbeChangeToNoop(v, 3 - flags, 1);
      sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
      sqlite3VdbeChangeP3(v, 2 + 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.
      */
      sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
      sqlite3VdbeChangeP2(v, 6, pTab->nCol);
      if( !db->mallocFailed ){
        sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
      }
    }
   
    sqlite3BtreeLeaveAll(db);
    rc = sqlite3SafetyOff(db);







>



|








>
>
>
>
>
>

|
<
<
|
|
|








|
|







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
      }
    }

    v = sqlite3VdbeCreate(db);
    if( v ){
      int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
      sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
      flags = !!flags;                 /* flags = (flags ? 1 : 0); */

      /* Configure the OP_Transaction */
      sqlite3VdbeChangeP1(v, 0, iDb);
      sqlite3VdbeChangeP2(v, 0, flags);

      /* Configure the OP_VerifyCookie */
      sqlite3VdbeChangeP1(v, 1, iDb);
      sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);

      /* Make sure a mutex is held on the table to be accessed */
      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 
      ** parameter of the other to pTab->tnum.  */


      sqlite3VdbeChangeToNoop(v, 4 - flags, 1);
      sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
      sqlite3VdbeChangeP3(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.
      */
      sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
      sqlite3VdbeChangeP2(v, 7, pTab->nCol);
      if( !db->mallocFailed ){
        sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
      }
    }
   
    sqlite3BtreeLeaveAll(db);
    rc = sqlite3SafetyOff(db);
Changes to test/incrblob2.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    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.10 2009/03/16 13:19:36 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable {!autovacuum || !pragma || !incrblob} {
  finish_test







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    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
270
271
272
273
274
275
276
277
278
279
280
281


282



283
284




285
286
287
288
289
290
291
  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 blob [db incrblob -readonly t1 data 1]
    catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2
  } {1 {database table is locked}}

  do_test incrblob2-5.6 {


    close $blob



    catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2
  } {0 {}}





  db2 close
  db close
  sqlite3_enable_shared_cache $::enable_shared_cache
}

#--------------------------------------------------------------------------







|
|
|


>
>
|
>
>
>


>
>
>
>







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  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
  sqlite3_enable_shared_cache $::enable_shared_cache
}

#--------------------------------------------------------------------------
Changes to test/types.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. Specfically
# it tests that the different storage classes (integer, real, text etc.)
# all work correctly.
#
# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Tests in this file are organized roughly as follows:
#
# types-1.*.*: Test that values are stored using the expected storage







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. Specfically
# it tests that the different storage classes (integer, real, text etc.)
# all work correctly.
#
# $Id: types.test,v 1.20 2009/06/29 06:00:37 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Tests in this file are organized roughly as follows:
#
# types-1.*.*: Test that values are stored using the expected storage
132
133
134
135
136
137
138

139
140
141
142
143
144
145
}

# Open the table with root-page $rootpage at the btree
# level. Return a list that is the length of each record
# in the table, in the tables default scanning order.
proc record_sizes {rootpage} {
  set bt [btree_open test.db 10 0]

  set c [btree_cursor $bt $rootpage 0]
  btree_first $c
  while 1 {
    lappend res [btree_payload_size $c]
    if {[btree_next $c]} break
  }
  btree_close_cursor $c







>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
}

# Open the table with root-page $rootpage at the btree
# level. Return a list that is the length of each record
# in the table, in the tables default scanning order.
proc record_sizes {rootpage} {
  set bt [btree_open test.db 10 0]
  btree_begin_transaction $bt
  set c [btree_cursor $bt $rootpage 0]
  btree_first $c
  while 1 {
    lappend res [btree_payload_size $c]
    if {[btree_next $c]} break
  }
  btree_close_cursor $c