/* * SQLite manages explicit transactions by setting a flag when a BEGIN; is * issued, then starting an actual transaction in the btree layer when the * first operation happens (a read txn if it's a read op, a write txn if write) * Then each statement will be contained in a sub-transaction. Since sequences * are implemented using a custom function, we need to emulate that * functionality. So there are three cases here: * - Not in an explicit transaction - start a statement, since we might do * write operations, and thus we need a valid statement_txn. * - In an explicit transaction, and the first statement. Start a txn and a statement txn. * - In an explicit transaction and not the first statemetn. Start a statement * transaction. * * The SQLite vdbe will take care of closing the statement transaction for us, * so we don't need to worry about that. * * Caching sequences can't be transactionally protected, so it's a no-op in * that case (and this function should not be called). * * It's safe to call this method multiple times since both * sqlite3BtreeBeginTrans and sqlite3BtreeBeginStmt are no-ops on subsequent * calls. */ static int btreeSeqStartTransaction( sqlite3_context *context, Btree *p, int is_write) { sqlite3 *db; Vdbe *vdbe; int rc; db = sqlite3_context_db_handle(context); /* * TODO: This is actually a linked list of VDBEs, not necessarily * the vdbe we want. I can't see a way to get the correct * vdbe handle. * It's possible that there is only one VDBE handle in DB, since * we use a shared cache. */ vdbe = db->pVdbe; if (!sqlite3BtreeIsInTrans(p) && (rc = btreeBeginTransInternal(p, 1)) != SQLITE_OK) { btreeSeqError(context, SQLITE_ERROR, "Could not begin transaction."); return (rc); } /* * TODO: Do we need logic bumping the VDBE statement count here? * vdbe.c:OP_Transaction does, but adding it here causes an * assert failure. It should be OK, because this code is only * really relevant in the case where there is a single statement. */ rc = sqlite3BtreeBeginStmt(p, vdbe->iStatement); return (rc); }
int btreeVacuum(Btree *p, char **pzErrMsg) { sqlite3 *db; int rc; u_int32_t truncatedPages; db = p->db; /* Return directly if vacuum is on progress */ if (p->inVacuum) return SQLITE_OK; /* * We're going to do updates in this transaction at the Berkeley DB * Core level (i.e., call DB->compact), but we start it read-only at * the SQL level to avoid overhead from checkpoint-on-commit. */ if ((rc = btreeBeginTransInternal(p, 0)) != SQLITE_OK) { sqlite3SetString(pzErrMsg, db, "failed to begin a vacuum transaction"); return rc; } p->inVacuum = 1; truncatedPages = 0; /* Go through all tables */ do { rc = btreeIncrVacuum(p, &truncatedPages); } while (rc == SQLITE_OK); p->needVacuum = 0; if (rc != SQLITE_DONE) { sqlite3SetString(pzErrMsg, db, "error during vacuum, rolled back"); (void)sqlite3BtreeRollback(p, SQLITE_OK); } else if ((rc = sqlite3BtreeCommit(p)) != SQLITE_OK) { sqlite3SetString(pzErrMsg, db, "failed to commit the vacuum transaction"); } p->inVacuum = 0; return rc; }
/* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage) { int returnCode, pages; Parse parse; DB_ENV *dbenv; BtShared *pBtDest, *pBtSrc; pBtDest = pBtSrc = NULL; if (p->rc != SQLITE_OK || nPage == 0) return p->rc; sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3_mutex_enter(p->pDestDb->mutex); /* * Make sure the schema has been read in, so the keyInfo * can be retrieved for the indexes. No-op if already read. * If the schema has not been read then an update must have * changed it, so backup will restart. */ memset(&parse, 0, sizeof(parse)); parse.db = p->pSrcDb; p->rc = sqlite3ReadSchema(&parse); if (p->rc != SQLITE_OK) goto err; /* * This process updated the source database, so * the backup process has to restart. */ if (p->pSrc->updateDuringBackup > p->lastUpdate) { p->rc = SQLITE_LOCKED; if ((p->rc = backupCleanup(p)) != SQLITE_OK) goto err; else backupReset(p); } pages = nPage; if (!p->cleaned) { const char *home; const char inmem[9] = ":memory:"; int storage; pBtDest = p->pDest->pBt; storage = p->pDest->pBt->dbStorage; if (storage == DB_STORE_NAMED) p->openDest = 1; if (strcmp(p->destName, "temp") == 0) p->pDest->schema = NULL; else p->pDestDb->aDb[p->iDb].pSchema = NULL; p->rc = btreeDeleteEnvironment(p->pDest, p->fullName, 1); if (storage == DB_STORE_INMEM && strcmp(p->destName, "temp") != 0) home = inmem; else home = p->fullName; if (p->rc != SQLITE_BUSY) p->pDest = p->pDestDb->aDb[p->iDb].pBt = NULL; if (p->rc != SQLITE_OK) goto err; /* * Call sqlite3OpenTempDatabase instead of * sqlite3BtreeOpen, because sqlite3OpenTempDatabase * automatically chooses the right flags before calling * sqlite3BtreeOpen. */ if (strcmp(p->destName, "temp") == 0) { memset(&parse, 0, sizeof(parse)); parse.db = p->pDestDb; p->rc = sqlite3OpenTempDatabase(&parse); p->pDest = p->pDestDb->aDb[p->iDb].pBt; if (p->pDest && p->iDb != 1) p->pDest->schema = p->pDestDb->aDb[p->iDb].pSchema; } else { p->rc = sqlite3BtreeOpen(NULL, home, p->pDestDb, &p->pDest, SQLITE_DEFAULT_CACHE_SIZE | SQLITE_OPEN_MAIN_DB, p->pDestDb->openFlags); p->pDestDb->aDb[p->iDb].pBt = p->pDest; if (p->pDest) { p->pDestDb->aDb[p->iDb].pSchema = sqlite3SchemaGet(p->pDestDb, p->pDest); if (p->pDestDb->aDb[p->iDb].pSchema == NULL) p->rc = SQLITE_NOMEM; } } if (p->pDest) p->pDest->nBackup++; #ifdef SQLITE_HAS_CODEC /* * In the case of a temporary source database, use the * encryption of the main database. */ if (strcmp(p->srcName, "temp") == 0) { int iDb = sqlite3FindDbName(p->pSrcDb, "main"); pBtSrc = p->pSrcDb->aDb[iDb].pBt->pBt; } else pBtSrc = p->pSrc->pBt; if (p->rc == SQLITE_OK) { if (p->iDb == 0) p->rc = sqlite3_key(p->pDestDb, pBtSrc->encrypt_pwd, pBtSrc->encrypt_pwd_len); else p->rc = sqlite3CodecAttach(p->pDestDb, p->iDb, pBtSrc->encrypt_pwd, pBtSrc->encrypt_pwd_len); } #endif if (p->rc != SQLITE_OK) goto err; p->cleaned = 1; } /* * Begin a transaction, unfortuantely the lock on * the schema has to be released to allow the sqlite_master * table to be cleared, which could allow another thread to * alter it, however accessing the backup database during * backup is already an illegal condition with undefined * results. */ if (!sqlite3BtreeIsInTrans(p->pDest)) { if (!p->pDest->connected) { p->rc = btreeOpenEnvironment(p->pDest, 1); if (p->rc != SQLITE_OK) goto err; } if ((p->rc = btreeBeginTransInternal(p->pDest, 2)) != SQLITE_OK) goto err; } /* Only this process should be accessing the backup environment. */ if (p->pDest->pBt->nRef > 1) { p->rc = SQLITE_BUSY; goto err; } /* * Begin a transaction, a lock error or update could have caused * it to be released in a previous call to step. */ if (!p->srcTxn) { dbenv = p->pSrc->pBt->dbenv; if ((p->rc = dberr2sqlite(dbenv->txn_begin(dbenv, p->pSrc->family_txn, &p->srcTxn, 0), NULL)) != SQLITE_OK) goto err; } /* * An update could have dropped or created a table, so recalculate * the list of tables. */ if (!p->tables) { if ((p->rc = btreeGetPageCount(p->pSrc, &p->tables, &p->nPagecount, p->srcTxn)) != SQLITE_OK) { sqlite3Error(p->pSrcDb, p->rc, 0); goto err; } p->nRemaining = p->nPagecount; } /* Copy the pages. */ p->rc = btreeCopyPages(p, &pages); if (p->rc == SQLITE_DONE) { p->nRemaining = 0; sqlite3ResetOneSchema(p->pDestDb, p->iDb); memset(&parse, 0, sizeof(parse)); parse.db = p->pDestDb; p->rc = sqlite3ReadSchema(&parse); if (p->rc == SQLITE_OK) p->rc = SQLITE_DONE; } else if (p->rc != SQLITE_OK) goto err; /* * The number of pages left to copy is an estimate, so * do not let the number go to zero unless we are really * done. */ if (p->rc != SQLITE_DONE) { if ((u32)pages >= p->nRemaining) p->nRemaining = 1; else p->nRemaining -= pages; } err: /* * This process updated the source database, so * the backup process has to restart. */ if (p->pSrc->updateDuringBackup > p->lastUpdate && (p->rc == SQLITE_OK || p->rc == SQLITE_DONE)) { int cleanCode; returnCode = p->rc; p->rc = SQLITE_LOCKED; if ((cleanCode = backupCleanup(p)) != SQLITE_OK) returnCode = p->rc = cleanCode; else backupReset(p); } else { returnCode = backupCleanup(p); if (returnCode == SQLITE_OK || (p->rc != SQLITE_OK && p->rc != SQLITE_DONE)) returnCode = p->rc; else p->rc = returnCode; } /* * On a locked or busy error the backup process is rolled back, * but can be restarted by the user. */ if ( returnCode == SQLITE_LOCKED || returnCode == SQLITE_BUSY ) backupReset(p); else if ( returnCode != SQLITE_OK && returnCode != SQLITE_DONE ) { sqlite3Error(p->pDestDb, p->rc, 0); } sqlite3_mutex_leave(p->pDestDb->mutex); sqlite3_mutex_leave(p->pSrcDb->mutex); return (returnCode); }