Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | When a connection disconnects from a shared-cache database, only delete the in-memory schema if there are no other connections. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | shared-schema |
Files: | files | file ages | folders |
SHA1: |
46f4eb5430d7bc9a339cdf7124ff4bd5 |
User & Date: | dan 2012-05-15 17:15:34.812 |
Context
2012-05-15
| ||
18:28 | The former sqlite3ResetInternalSchema() routine was really two different routines, selected by parameter, each with a confused mission. So split this routine up into three separate smaller routines, calling each separately as needed. Hopefully this will make further refactoring and schema reset collateral damage containment easier. (check-in: aa0c3493d3 user: drh tags: shared-schema) | |
17:15 | When a connection disconnects from a shared-cache database, only delete the in-memory schema if there are no other connections. (check-in: 46f4eb5430 user: dan tags: shared-schema) | |
12:49 | Add assert()s to verify that Table objects in the schema never use lookaside memory. (check-in: 736d6ea677 user: drh tags: trunk) | |
Changes
Changes to src/main.c.
︙ | ︙ | |||
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | pDestructor->nRef--; if( pDestructor->nRef==0 ){ pDestructor->xDestroy(pDestructor->pUserData); sqlite3DbFree(db, pDestructor); } } } /* ** Close an existing SQLite database */ int sqlite3_close(sqlite3 *db){ HashElem *i; /* Hash table iterator */ int j; if( !db ){ return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); | > > > > > > > > > > > > > > > > > > > > > > > > | | | | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 | pDestructor->nRef--; if( pDestructor->nRef==0 ){ pDestructor->xDestroy(pDestructor->pUserData); sqlite3DbFree(db, pDestructor); } } } /* ** Disconnect all sqlite3_vtab objects that belong to database connection ** db. This is called when db is being closed. */ static void disconnectAllVtab(sqlite3 *db){ #ifndef SQLITE_OMIT_VIRTUALTABLE int i; sqlite3BtreeEnterAll(db); for(i=0; i<db->nDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( db->aDb[i].pSchema ){ HashElem *p; for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ Table *pTab = (Table *)sqliteHashData(p); if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab); } } } sqlite3BtreeLeaveAll(db); #else UNUSED_PARAMETER(db); #endif } /* ** Close an existing SQLite database */ int sqlite3_close(sqlite3 *db){ HashElem *i; /* Hash table iterator */ int j; if( !db ){ return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); /* Force xDisconnect calls on all virtual tables */ disconnectAllVtab(db); /* If a transaction is open, the disconnectAllVtab() call above ** will not have called the xDisconnect() method on any virtual ** tables in the db->aVTrans[] array. The following sqlite3VtabRollback() ** call will do so. We need to do this before the check for active ** SQL statements below, as the v-table implementation may be storing ** some prepared statements internally. */ sqlite3VtabRollback(db); |
︙ | ︙ | |||
775 776 777 778 779 780 781 782 783 784 785 786 787 788 | sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; } } } sqlite3ResetInternalSchema(db, -1); /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. */ sqlite3ConnectionClosed(db); | > > > > > < < | 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; } } } /* This call frees the schema associated with the temp database only (if ** any). It also frees the db->aDb array, if required. */ sqlite3ResetInternalSchema(db, -1); assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. */ sqlite3ConnectionClosed(db); for(j=0; j<ArraySize(db->aFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ pHash = p->pHash; while( p ){ functionDestroy(db, p); pNext = p->pNext; |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 | # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) # define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK # define sqlite3GetVTable(X,Y) ((VTable*)0) #else void sqlite3VtabClear(sqlite3 *db, Table*); int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabRollback(sqlite3 *db); int sqlite3VtabCommit(sqlite3 *db); void sqlite3VtabLock(VTable *); void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); | > | 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 | # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) # define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK # define sqlite3GetVTable(X,Y) ((VTable*)0) #else void sqlite3VtabClear(sqlite3 *db, Table*); void sqlite3VtabDisconnect(sqlite3 *db, Table *p); int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabRollback(sqlite3 *db); int sqlite3VtabCommit(sqlite3 *db); void sqlite3VtabLock(VTable *); void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 | } pVTable = pNext; } assert( !db || pRet ); return pRet; } /* ** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. ** ** This function may only be called when the mutexes associated with all ** shared b-tree databases opened using connection db are held by the | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } pVTable = pNext; } assert( !db || pRet ); return pRet; } /* ** Table *p is a virtual table. This function removes the VTable object ** for table *p associated with database connection db from the linked ** list in p->pVTab. It also decrements the VTable ref count. This is ** used when closing database connection db to free all of its VTable ** objects without disturbing the rest of the Schema object (which may ** be being used by other shared-cache connections). */ void sqlite3VtabDisconnect(sqlite3 *db, Table *p){ VTable **ppVTab; assert( IsVirtual(p) ); assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3_mutex_held(db->mutex) ); for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ if( (*ppVTab)->db==db ){ VTable *pVTab = *ppVTab; *ppVTab = pVTab->pNext; sqlite3VtabUnlock(pVTab); break; } } } /* ** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. ** ** This function may only be called when the mutexes associated with all ** shared b-tree databases opened using connection db are held by the |
︙ | ︙ |
Changes to test/capi3.test.
︙ | ︙ | |||
645 646 647 648 649 650 651 652 653 | set STMT [sqlite3_prepare $DB $sql -1 TAIL] expr 0 } {0} do_test capi3-6.1 { db cache flush sqlite3_close $DB } {SQLITE_BUSY} do_test capi3-6.2 { sqlite3_step $STMT | > > > > | | > | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 | set STMT [sqlite3_prepare $DB $sql -1 TAIL] expr 0 } {0} do_test capi3-6.1 { db cache flush sqlite3_close $DB } {SQLITE_BUSY} # 6.2 and 6.3 used to return SQLITE_ERROR and SQLITE_SCHEMA, respectively. # But since attempting to close a connection no longer resets the internal # schema and expires all statements, this is no longer the case. do_test capi3-6.2 { sqlite3_step $STMT } {SQLITE_ROW} #check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1} do_test capi3-6.3 { sqlite3_finalize $STMT } {SQLITE_OK} do_test capi3-6.4-misuse { db cache flush sqlite3_close $DB } {SQLITE_OK} db close # This procedure sets the value of the file-format in file 'test.db' |
︙ | ︙ |
Added test/shared8.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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | # 2012 May 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The tests in this file are intended to show that closing one database # connection to a shared-cache while there exist other connections (a) # does not cause the schema to be reloaded and (b) does not cause any # other problems. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !shared_cache { finish_test ; return } set testprefix shared8 db close set ::enable_shared_cache [sqlite3_enable_shared_cache 1] do_test 0.0 { sqlite3_enable_shared_cache } {1} proc roman {n} { array set R {1 i 2 ii 3 iii 4 iv 5 v 6 vi 7 vii 8 viii 9 ix 10 x} set R($n) } #------------------------------------------------------------------------- # The following tests work as follows: # # 1.0: Open connection [db1] and populate the database. # # 1.1: Using "PRAGMA writable_schema", destroy the database schema on # disk. The schema is still in memory, so it is possible to keep # using it, but any attempt to reload it from disk will fail. # # 1.3-4: Open connection db2. Check that it can see the db schema. Then # close db1 and check that db2 still works. This shows that closing # db1 did not reset the in-memory schema. # # 1.5-7: Similar to 1.3-4. # # 1.8: Close all database connections (deleting the in-memory schema). # Then open a new connection and check that it cannot read the db. # do_test 1.0 { sqlite3 db1 test.db db1 func roman roman execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 2); INSERT INTO t1 VALUES(3, 3); INSERT INTO t1 VALUES(4, 4); CREATE VIEW v1 AS SELECT a, roman(b) FROM t1; SELECT * FROM v1; } db1 } {1 i 2 ii 3 iii 4 iv} do_test 1.1 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE 1; PRAGMA writable_schema = 0; SELECT * FROM sqlite_master; } db1 } {} do_test 1.2 { execsql { SELECT * FROM v1 } db1 } {1 i 2 ii 3 iii 4 iv} do_test 1.3 { sqlite3 db2 test.db db2 func roman roman execsql { SELECT * FROM v1 } db2 } {1 i 2 ii 3 iii 4 iv} do_test 1.4 { db1 close execsql { SELECT * FROM v1 } db2 } {1 i 2 ii 3 iii 4 iv} do_test 1.5 { sqlite3 db3 test.db db3 func roman roman execsql { SELECT * FROM v1 } db3 } {1 i 2 ii 3 iii 4 iv} do_test 1.6 { execsql { SELECT * FROM v1 } db2 } {1 i 2 ii 3 iii 4 iv} do_test 1.7 { db2 close execsql { SELECT * FROM v1 } db3 } {1 i 2 ii 3 iii 4 iv} do_test 1.8 { db3 close sqlite3 db4 test.db catchsql { SELECT * FROM v1 } db4 } {1 {no such table: v1}} foreach db {db1 db2 db3 db4} { catch { $db close } } sqlite3_enable_shared_cache $::enable_shared_cache finish_test |