SQLite

Check-in [8f1423445b]
Login

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

Overview
Comment:Fix a problem causing the BtShared.isPending flag to be cleared to early. Also coverage improvements for btree.c. (CVS 6440)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8f1423445b29a5f52ed907de6db82128a96ebfe2
User & Date: danielk1977 2009-04-02 18:28:08.000
Context
2009-04-02
18:32
Fix the sqlite3_prepare() family of interfaces so that they zero the *ppStmt value even on an SQLITE_MISUSE return. Make it clear in the documentation that the ppStmt parameter cannot be zero. (CVS 6441) (check-in: 23bf9f2665 user: drh tags: trunk)
18:28
Fix a problem causing the BtShared.isPending flag to be cleared to early. Also coverage improvements for btree.c. (CVS 6440) (check-in: 8f1423445b user: danielk1977 tags: trunk)
17:23
Ensure the required VerifyCookie/Transaction/TableLock opcodes are added for "x IN (SELECT c FROM t)" expressions. Ticket #3771. (CVS 6439) (check-in: 058a2f2093 user: danielk1977 tags: trunk)
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.587 2009/04/01 19:07:04 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"












|







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.588 2009/04/02 18:28:08 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"

244
245
246
247
248
249
250




251
252
253
254
255
256
257

258
259
260
261

262
263
264
265
266
267
268
}
#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 handle p.




*/
static void clearAllSharedCacheTableLocks(Btree *p){
  BtShared *pBt = p->pBt;
  BtLock **ppIter = &pBt->pLock;

  assert( sqlite3BtreeHoldsMutex(p) );
  assert( p->sharable || 0==*ppIter );


  while( *ppIter ){
    BtLock *pLock = *ppIter;
    assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );

    if( pLock->pBtree==p ){
      *ppIter = pLock->pNext;
      sqlite3_free(pLock);
    }else{
      ppIter = &pLock->pNext;
    }
  }







>
>
>
>







>




>







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
}
#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 handle p.
**
** This function assumes that handle p has an open read or write 
** transaction. If it does not, then the BtShared.isPending variable
** may be incorrectly cleared.
*/
static void clearAllSharedCacheTableLocks(Btree *p){
  BtShared *pBt = p->pBt;
  BtLock **ppIter = &pBt->pLock;

  assert( sqlite3BtreeHoldsMutex(p) );
  assert( p->sharable || 0==*ppIter );
  assert( p->inTrans>0 );

  while( *ppIter ){
    BtLock *pLock = *ppIter;
    assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
    assert( pLock->pBtree->inTrans>=pLock->eLock );
    if( pLock->pBtree==p ){
      *ppIter = pLock->pNext;
      sqlite3_free(pLock);
    }else{
      ppIter = &pLock->pNext;
    }
  }
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271

set_child_ptrmaps_out:
  pPage->isInit = isInitOrig;
  return rc;
}

/*
** Somewhere on pPage, which is guarenteed to be a btree page, not an overflow
** page, 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.
**







|







2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277

set_child_ptrmaps_out:
  pPage->isInit = isInitOrig;
  return rc;
}

/*
** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow
** page, 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.
**
2420
2421
2422
2423
2424
2425
2426

2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
** number of pages the database file will contain after this 
** process is complete.
*/
static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
  Pgno nFreeList;           /* Number of pages still on the free-list */

  assert( sqlite3_mutex_held(pBt->mutex) );


  if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){
    int rc;
    u8 eType;
    Pgno iPtrPage;

    nFreeList = get4byte(&pBt->pPage1->aData[36]);
    if( nFreeList==0 || nFin==iLastPg ){
      return SQLITE_DONE;
    }

    rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage);
    if( rc!=SQLITE_OK ){
      return rc;
    }







>







|







2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
** number of pages the database file will contain after this 
** process is complete.
*/
static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
  Pgno nFreeList;           /* Number of pages still on the free-list */

  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( iLastPg>nFin );

  if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){
    int rc;
    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;
    }
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698

2699
2700
2701
2702
2703
2704
2705
    rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
    if( rc!=SQLITE_OK ){
      sqlite3BtreeLeave(p);
      return rc;
    }
    pBt->inTransaction = TRANS_READ;
  }
  clearAllSharedCacheTableLocks(p);

  /* If the handle has 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 ){

    pBt->nTransaction--;
    if( 0==pBt->nTransaction ){
      pBt->inTransaction = TRANS_NONE;
    }
  }

  /* Set the handles current transaction state to TRANS_NONE and unlock







<







>







2691
2692
2693
2694
2695
2696
2697

2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
    rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
    if( rc!=SQLITE_OK ){
      sqlite3BtreeLeave(p);
      return rc;
    }
    pBt->inTransaction = TRANS_READ;
  }


  /* If the handle has 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 handles current transaction state to TRANS_NONE and unlock
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
    ** 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.
    */
    sqlite3BtreeTripAllCursors(p, rc);
  }
#endif
  btreeIntegrity(p);
  clearAllSharedCacheTableLocks(p);

  if( p->inTrans==TRANS_WRITE ){
    int rc2;

    assert( TRANS_WRITE==pBt->inTransaction );
    rc2 = sqlite3PagerRollback(pBt->pPager);
    if( rc2!=SQLITE_OK ){







<







2815
2816
2817
2818
2819
2820
2821

2822
2823
2824
2825
2826
2827
2828
    ** 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.
    */
    sqlite3BtreeTripAllCursors(p, rc);
  }
#endif
  btreeIntegrity(p);


  if( p->inTrans==TRANS_WRITE ){
    int rc2;

    assert( TRANS_WRITE==pBt->inTransaction );
    rc2 = sqlite3PagerRollback(pBt->pPager);
    if( rc2!=SQLITE_OK ){
2830
2831
2832
2833
2834
2835
2836

2837
2838
2839
2840
2841
2842
2843
      releasePage(pPage1);
    }
    assert( countWriteCursors(pBt)==0 );
    pBt->inTransaction = TRANS_READ;
  }

  if( p->inTrans!=TRANS_NONE ){

    assert( pBt->nTransaction>0 );
    pBt->nTransaction--;
    if( 0==pBt->nTransaction ){
      pBt->inTransaction = TRANS_NONE;
    }
  }








>







2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
      releasePage(pPage1);
    }
    assert( countWriteCursors(pBt)==0 );
    pBt->inTransaction = TRANS_READ;
  }

  if( p->inTrans!=TRANS_NONE ){
    clearAllSharedCacheTableLocks(p);
    assert( pBt->nTransaction>0 );
    pBt->nTransaction--;
    if( 0==pBt->nTransaction ){
      pBt->inTransaction = TRANS_NONE;
    }
  }

6784
6785
6786
6787
6788
6789
6790

6791




6792

6793
6794
6795
6796
6797
6798
6799
  /* If autovacuumed is disabled in this build but we are trying to 
  ** access an autovacuumed database, then make the database readonly. 
  */
#ifdef SQLITE_OMIT_AUTOVACUUM
  if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
#endif


  /* Grab the read-lock on page 1. */




  rc = setSharedCacheTableLock(p, 1, READ_LOCK);

  sqlite3BtreeLeave(p);
  return rc;
}

/*
** Write meta-information back into the database.  Meta[0] is
** read-only and may not be written.







>
|
>
>
>
>
|
>







6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
  /* If autovacuumed is disabled in this build but we are trying to 
  ** access an autovacuumed database, then make the database readonly. 
  */
#ifdef SQLITE_OMIT_AUTOVACUUM
  if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
#endif

  /* If there is currently an open transaction, grab a read-lock 
  ** on page 1 of the database file. This is done to make sure that
  ** no other connection can modify the meta value just read from
  ** the database until the transaction is concluded.
  */
  if( p->inTrans>0 ){
    rc = setSharedCacheTableLock(p, 1, READ_LOCK);
  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*
** Write meta-information back into the database.  Meta[0] is
** read-only and may not be written.
Changes to test/corrupt2.test.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.
#
# $Id: corrupt2.test,v 1.18 2008/09/29 11:49:48 danielk1977 Exp $

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

# The following tests - corrupt2-1.* - create some databases corrupted in
# specific ways and ensure that SQLite detects them as corrupt.
#







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.
#
# $Id: corrupt2.test,v 1.19 2009/04/02 18:28:08 danielk1977 Exp $

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

# The following tests - corrupt2-1.* - create some databases corrupted in
# specific ways and ensure that SQLite detects them as corrupt.
#
450
451
452
453
454
455
456
457












































458
  do_test corrupt2-10.1 {
    catchsql { SELECT * FROM t2 }
  } {1 {malformed database schema (t2)}}
  do_test corrupt2-10.2 {
    sqlite3_errcode db
  } {SQLITE_CORRUPT}
}













































finish_test








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

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  do_test corrupt2-10.1 {
    catchsql { SELECT * FROM t2 }
  } {1 {malformed database schema (t2)}}
  do_test corrupt2-10.2 {
    sqlite3_errcode db
  } {SQLITE_CORRUPT}
}

corruption_test -sqlprep {
  PRAGMA auto_vacuum = incremental;
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, randstr(100,100));
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t2 SELECT * FROM t1;
  DELETE FROM t1;
} -corrupt {
  set offset [expr [file size corrupt.db] - 1024]
  hexio_write corrupt.db $offset FF 
  hexio_write corrupt.db 24   12345678
} -test {
  do_test corrupt2-11.1 {
    catchsql { PRAGMA incremental_vacuum }
  } {1 {database disk image is malformed}}
}

corruption_test -sqlprep {
  PRAGMA auto_vacuum = incremental;
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, randstr(100,100));
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
  INSERT INTO t2 SELECT * FROM t1;
  DELETE FROM t1;
} -corrupt {
  set pgno [expr [file size corrupt.db] / 1024]
  hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03 
  hexio_write corrupt.db 24   12345678
} -test {
  do_test corrupt2-12.1 {
    catchsql { PRAGMA incremental_vacuum }
  } {1 {database disk image is malformed}}
}

finish_test
Changes to test/shared6.test.
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
# 2009 April 01
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: shared6.test,v 1.2 2009/04/01 18:25:54 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !shared_cache { finish_test ; return }

do_test shared6-1.1.1 {
  execsql {
    CREATE TABLE t1(a, b);
    CREATE TABLE t2(c, d);
    CREATE TABLE t3(e, f);
  }

} {}
do_test shared6-1.1.2 {
  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
  sqlite3_enable_shared_cache
} {1}

do_test shared6-1.1.3 {











|











>







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
# 2009 April 01
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: shared6.test,v 1.3 2009/04/02 18:28:08 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !shared_cache { finish_test ; return }

do_test shared6-1.1.1 {
  execsql {
    CREATE TABLE t1(a, b);
    CREATE TABLE t2(c, d);
    CREATE TABLE t3(e, f);
  }
  db close
} {}
do_test shared6-1.1.2 {
  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
  sqlite3_enable_shared_cache
} {1}

do_test shared6-1.1.3 {
129
130
131
132
133
134
135

136
137
138

139
140
141
142
143
144
145
} {1 {database table is locked: t1}}

do_test shared6-1.X {
  db1 close
  db2 close
} {}


# The following tests - shared6-2.* - test that two database connections
# that connect to the same file using different VFS implementations do
# not share a cache.

if {$::tcl_platform(platform) eq "unix"} {
  do_test shared6-2.1 {
    sqlite3 db1 test.db -vfs unix
    sqlite3 db2 test.db -vfs unix
    sqlite3 db3 test.db -vfs unix-none
    sqlite3 db4 test.db -vfs unix-none
  } {}







>



>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
} {1 {database table is locked: t1}}

do_test shared6-1.X {
  db1 close
  db2 close
} {}

#-------------------------------------------------------------------------
# The following tests - shared6-2.* - test that two database connections
# that connect to the same file using different VFS implementations do
# not share a cache.
#
if {$::tcl_platform(platform) eq "unix"} {
  do_test shared6-2.1 {
    sqlite3 db1 test.db -vfs unix
    sqlite3 db2 test.db -vfs unix
    sqlite3 db3 test.db -vfs unix-none
    sqlite3 db4 test.db -vfs unix-none
  } {}
169
170
171
172
173
174
175
176










































































177
178
179
  do_test shared6-2.X {
    db1 close
    db2 close
    db3 close
    db4 close
  } {}
}











































































sqlite3_enable_shared_cache $::enable_shared_cache
finish_test









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



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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  do_test shared6-2.X {
    db1 close
    db2 close
    db3 close
    db4 close
  } {}
}

#-------------------------------------------------------------------------
# Test that it is possible to open an exclusive transaction while 
# already holding a read-lock on the database file. And that it is
# not possible if some other connection holds such a lock.
#
do_test shared6-3.1 {
  sqlite3 db1 test.db
  sqlite3 db2 test.db
  sqlite3 db3 test.db
} {}
db1 eval {SELECT * FROM t1} {
  # Within this block [db1] is holding a read-lock on t1. Test that
  # this means t1 cannot be written by [db2].
  #
  do_test shared6-3.2 {
    catchsql { INSERT INTO t1 VALUES(1, 2) } db2
  } {1 {database table is locked: t1}}

  do_test shared6-3.3 {
    execsql { BEGIN EXCLUSIVE } db1
  } {}
  break
}
do_test shared6-3.4 {
  catchsql { SELECT * FROM t1 } db2
} {1 {database schema is locked: main}}
do_test shared6-3.5 {
  execsql COMMIT db1
} {}
db2 eval {SELECT * FROM t1} {
  do_test shared6-3.6 {
    catchsql { BEGIN EXCLUSIVE } db1
  } {1 {database table is locked}}
  break
}
do_test shared6-3.7 {
  execsql { BEGIN } db1
  execsql { BEGIN } db2
} {}
db2 eval {SELECT * FROM t1} {
  do_test shared6-3.8 {
    catchsql { INSERT INTO t1 VALUES(1, 2) } db1
  } {1 {database table is locked: t1}}
  break
}
do_test shared6-3.9 {
  execsql { BEGIN ; ROLLBACK } db3
} {}
do_test shared6-3.10 {
  catchsql { SELECT * FROM t1 } db3
} {1 {database table is locked}}
do_test shared6-3.X {
  db1 close
  db2 close
  db3 close
} {}

do_test shared6-4.1 {
  #file delete -force test.db test.db-journal
  sqlite3 db1 test.db
  sqlite3 db2 test.db

  set ::STMT [sqlite3_prepare_v2 db1 "SELECT * FROM t1" -1 DUMMY]
  execsql { CREATE TABLE t5(a, b) } db2
} {}
do_test shared6-4.2 {
  sqlite3_finalize $::STMT
} {SQLITE_OK}
do_test shared6-4.X {
  
  db1 close
  db2 close
} {}

sqlite3_enable_shared_cache $::enable_shared_cache
finish_test