Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Begin a series of changes designed to reduce the scope and frequency of invalidating schemas. Design goals are that the internal schema should never be deleted out from under a prepared statement that is running and that all prepared statements should be expired if the schema is invalidated. At the same time, minimize the number of schema invalidations. This change merely revises the sqlite3ResetInternalSchema() function to use -1 as the wildcard for "all" rather than 0, so that we can reset the main schema independently of all the others. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | schema-parse-refactor |
Files: | files | file ages | folders |
SHA1: |
6a8ad6e31e0d2b8a5ace38faf0efd805 |
User & Date: | drh 2011-04-02 16:28:52.034 |
Context
2011-04-02
| ||
16:50 | When resetting any non-TEMP schema, also reset the TEMP schema since it might be holding references to the non-TEMP schema that just got reset. (check-in: 211d5dde1f user: drh tags: schema-parse-refactor) | |
16:28 | Begin a series of changes designed to reduce the scope and frequency of invalidating schemas. Design goals are that the internal schema should never be deleted out from under a prepared statement that is running and that all prepared statements should be expired if the schema is invalidated. At the same time, minimize the number of schema invalidations. This change merely revises the sqlite3ResetInternalSchema() function to use -1 as the wildcard for "all" rather than 0, so that we can reset the main schema independently of all the others. (check-in: 6a8ad6e31e user: drh tags: schema-parse-refactor) | |
09:44 | Fix a race condition in OP_ParseSchema. (check-in: 71a799b02a user: dan tags: trunk) | |
Changes
Changes to src/attach.c.
︙ | ︙ | |||
196 197 198 199 200 201 202 | int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ sqlite3BtreeClose(db->aDb[iDb].pBt); db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } | | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ sqlite3BtreeClose(db->aDb[iDb].pBt); db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } sqlite3ResetInternalSchema(db, -1); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; sqlite3DbFree(db, zErrDyn); zErrDyn = sqlite3MPrintf(db, "out of memory"); }else if( zErrDyn==0 ){ zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); |
︙ | ︙ | |||
268 269 270 271 272 273 274 | sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); goto detach_error; } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; | | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); goto detach_error; } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; sqlite3ResetInternalSchema(db, -1); return; detach_error: sqlite3_result_error(context, zErr, -1); } /* |
︙ | ︙ |
Changes to src/backup.c.
︙ | ︙ | |||
397 398 399 400 401 402 403 | */ if( rc==SQLITE_DONE && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK ){ int nDestTruncate; if( p->pDestDb ){ | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | */ if( rc==SQLITE_DONE && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK ){ int nDestTruncate; if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, -1); } /* Set nDestTruncate to the final number of pages in the destination ** database. The complication here is that the destination page ** size may be different to the source page size. ** ** If the source page size is smaller than the destination page size, |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
389 390 391 392 393 394 395 | /* ** Erase all schema information from the in-memory hash tables of ** a single database. This routine is called to reclaim memory ** before the database closes. It is also called during a rollback ** if there were schema changes during the transaction or if a ** schema-cookie mismatch occurs. ** | | | | | > > > | > | > > > > > > | < < | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | /* ** Erase all schema information from the in-memory hash tables of ** a single database. This routine is called to reclaim memory ** before the database closes. It is also called during a rollback ** if there were schema changes during the transaction or if a ** schema-cookie mismatch occurs. ** ** If iDb<0 then reset the internal schema tables for all database ** files. If iDb>=0 then reset the internal schema for only the ** single file indicated. */ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ int i, j; assert( iDb<db->nDb ); if( iDb>=0 ){ /* Case 1: Reset the single schema identified by iDb */ Db *pDb = &db->aDb[iDb]; if( pDb->pSchema ){ assert(iDb==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt))); sqlite3SchemaFree(pDb->pSchema); } return; } /* Case 2 (from here to the end): Reset all schemas for all attached ** databases. */ assert( iDb<0 ); sqlite3BtreeEnterAll(db); for(i=0; i<db->nDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt))); sqlite3SchemaFree(pDb->pSchema); } } db->flags &= ~SQLITE_InternChanges; sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); /* If one or more of the auxiliary database files has been closed, ** then remove them from the auxiliary database list. We take the ** opportunity to do this here since we have just deleted all of the |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
683 684 685 686 687 688 689 | return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); | | | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); sqlite3ResetInternalSchema(db, -1); /* If a transaction is open, the ResetInternalSchema() 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. |
︙ | ︙ | |||
726 727 728 729 730 731 732 | sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; } } } | | | 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 | 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); assert( db->nDb<=2 ); |
︙ | ︙ | |||
817 818 819 820 821 822 823 | } } sqlite3VtabRollback(db); sqlite3EndBenignMalloc(); if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); | | | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 | } } sqlite3VtabRollback(db); sqlite3EndBenignMalloc(); if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, -1); } /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
111 112 113 114 115 116 117 | if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){ sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " "from within a transaction"); return SQLITE_ERROR; } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){ sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " "from within a transaction"); return SQLITE_ERROR; } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; sqlite3ResetInternalSchema(db, -1); } return SQLITE_OK; } #endif /* SQLITE_PAGER_PRAGMAS */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS /* |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
334 335 336 337 338 339 340 | if( rc==SQLITE_OK ){ sqlite3AnalysisLoad(db, iDb); } #endif } if( db->mallocFailed ){ rc = SQLITE_NOMEM; | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | if( rc==SQLITE_OK ){ sqlite3AnalysisLoad(db, iDb); } #endif } if( db->mallocFailed ){ rc = SQLITE_NOMEM; sqlite3ResetInternalSchema(db, -1); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider ** the schema loaded, even if errors occurred. In this situation the ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset ** of the schema was loaded before the error occurred. The primary |
︙ | ︙ | |||
609 610 611 612 613 614 615 | pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; if( pParse->checkSchema ){ schemaIsValid(pParse); } if( pParse->rc==SQLITE_SCHEMA ){ | | | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; if( pParse->checkSchema ){ schemaIsValid(pParse); } if( pParse->rc==SQLITE_SCHEMA ){ sqlite3ResetInternalSchema(db, -1); } if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } if( pzTail ){ *pzTail = pParse->zTail; } |
︙ | ︙ |
Changes to src/vacuum.c.
︙ | ︙ | |||
331 332 333 334 335 336 337 | if( pDb ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } | | > | 331 332 333 334 335 336 337 338 339 340 341 342 343 | if( pDb ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } sqlite3ResetInternalSchema(db, -1); return rc; } #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ |
Changes to src/vdbe.c.
︙ | ︙ | |||
2645 2646 2647 2648 2649 2650 2651 | rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); | | | 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 | rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=pSavepoint ){ |
︙ | ︙ | |||
5942 5943 5944 5945 5946 5947 5948 | p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", pc, p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; | | | 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 | p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", pc, p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, -1); /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: sqlite3BtreeMutexArrayLeave(&p->aMutex); return rc; |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2187 2188 2189 2190 2191 2192 2193 | sqlite3VdbeSetChanges(db, 0); } p->nChange = 0; } /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ | | | 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 | sqlite3VdbeSetChanges(db, 0); } p->nChange = 0; } /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ sqlite3BtreeMutexArrayLeave(&p->aMutex); } |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
44 45 46 47 48 49 50 | if( pDel && pDel->xDestroy ){ pDel->xDestroy(pDel->pAux); } sqlite3DbFree(db, pDel); if( pDel==pMod ){ db->mallocFailed = 1; } | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | if( pDel && pDel->xDestroy ){ pDel->xDestroy(pDel->pAux); } sqlite3DbFree(db, pDel); if( pDel==pMod ){ db->mallocFailed = 1; } sqlite3ResetInternalSchema(db, -1); }else if( xDestroy ){ xDestroy(pAux); } rc = sqlite3ApiExit(db, SQLITE_OK); sqlite3_mutex_leave(db->mutex); return rc; } |
︙ | ︙ |