void SQLTransaction::postflightAndCommit() { ASSERT(m_lockAcquired); // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPostflight(this)) { m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) { m_database->reportCommitTransactionResult(3, SQLError::UNKNOWN_ERR, 0); m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight"); } handleTransactionError(false); return; } // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails ASSERT(m_sqliteTransaction); m_database->disableAuthorizer(); m_sqliteTransaction->commit(); m_database->enableAuthorizer(); // If the commit failed, the transaction will still be marked as "in progress" if (m_sqliteTransaction->inProgress()) { if (m_wrapper) m_wrapper->handleCommitFailedAfterPostflight(this); m_successCallbackWrapper.clear(); m_database->reportCommitTransactionResult(4, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError()); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); handleTransactionError(false); return; } m_database->reportCommitTransactionResult(0, -1, 0); // OK // Vacuum the database if anything was deleted. if (m_database->hadDeletes()) m_database->incrementalVacuumIfNeeded(); // The commit was successful. If the transaction modified this database, notify the delegates. if (m_modifiedDatabase) m_database->transactionClient()->didCommitWriteTransaction(database()); // Now release our unneeded callbacks, to break reference cycles. m_errorCallbackWrapper.clear(); // Transaction Step 10 - Deliver success callback, if there is one if (m_successCallbackWrapper.hasCallback()) { m_nextStep = &SQLTransaction::deliverSuccessCallback; LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); } else cleanupAfterSuccessCallback(); }
void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); // If the database was deleted, jump to the error callback if (m_database->deleted()) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); handleTransactionError(false); return; } // Set the maximum usage for this transaction if this transactions is not read-only if (!m_readOnly) m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); ASSERT(!m_sqliteTransaction); m_sqliteTransaction.set(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); m_database->resetDeletes(); m_database->disableAuthorizer(); m_sqliteTransaction->begin(); m_database->enableAuthorizer(); // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); m_sqliteTransaction.clear(); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to open a transaction to the database"); handleTransactionError(false); return; } // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(this)) { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); m_sqliteTransaction.clear(); m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); handleTransactionError(false); return; } // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object m_nextStep = &SQLTransaction::deliverTransactionCallback; LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); }
void SQLTransaction::postflightAndCommit() { ASSERT(m_lockAcquired); // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPostflight(this)) { m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(0, "unknown error occured setting up transaction"); handleTransactionError(false); return; } // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails ASSERT(m_sqliteTransaction); m_database->m_databaseAuthorizer->disable(); m_sqliteTransaction->commit(); m_database->m_databaseAuthorizer->enable(); // If the commit failed, the transaction will still be marked as "in progress" if (m_sqliteTransaction->inProgress()) { m_transactionError = SQLError::create(0, "failed to commit the transaction"); handleTransactionError(false); return; } // The commit was successful. If the transaction modified this database, // vacuum the database if needed and notify the delegates. if (m_modifiedDatabase) { m_database->incrementalVacuumIfNeeded(); m_database->transactionClient()->didCommitTransaction(this); } // Now release our unneeded callbacks, to break reference cycles. m_callback = 0; m_errorCallback = 0; // Transaction Step 10 - Deliver success callback, if there is one if (m_successCallback) { m_nextStep = &SQLTransaction::deliverSuccessCallback; LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); } else cleanupAfterSuccessCallback(); }
void SQLTransaction::postflightAndCommit() { ASSERT(m_lockAcquired); // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail. if (m_wrapper && !m_wrapper->performPostflight(*this)) { m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight"); handleTransactionError(); return; } // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails. ASSERT(m_sqliteTransaction); m_database->disableAuthorizer(); m_sqliteTransaction->commit(); m_database->enableAuthorizer(); releaseOriginLockIfNeeded(); // If the commit failed, the transaction will still be marked as "in progress" if (m_sqliteTransaction->inProgress()) { if (m_wrapper) m_wrapper->handleCommitFailedAfterPostflight(*this); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); handleTransactionError(); return; } // Vacuum the database if anything was deleted. if (m_database->hadDeletes()) m_database->incrementalVacuumIfNeeded(); // The commit was successful. If the transaction modified this database, notify the delegates. if (m_modifiedDatabase) m_database->didCommitWriteTransaction(); // Spec 4.3.2.8: Deliver success callback, if there is one. scheduleCallback(&SQLTransaction::deliverSuccessCallback); }
void SQLTransaction::handleCurrentStatementError() { // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, // or the transaction was rolled back, jump to the transaction error callback if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { m_nextStep = &SQLTransaction::deliverStatementCallback; LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); } else { m_transactionError = m_currentStatement->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); handleTransactionError(false); } }
void SQLTransaction::handleCurrentStatementError() { // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback, // or the transaction was rolled back, jump to the transaction error callback if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { scheduleCallback(&SQLTransaction::deliverStatementCallback); return; } m_transactionError = m_currentStatement->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); handleTransactionError(); }
void SQLTransaction::deliverStatementCallback() { ASSERT(m_currentStatement); // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback // Otherwise, continue to loop through the statement queue m_executeSqlAllowed = true; bool result = m_currentStatement->performCallback(this); m_executeSqlAllowed = false; if (result) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); handleTransactionError(true); } else scheduleToRunStatements(); }
void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); // If the database was deleted, jump to the error callback if (m_database->deleted()) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); handleTransactionError(false); return; } // Set the maximum usage for this transaction if this transactions is not read-only if (!m_readOnly) m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); ASSERT(!m_sqliteTransaction); m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); m_database->resetDeletes(); m_database->disableAuthorizer(); m_sqliteTransaction->begin(); m_database->enableAuthorizer(); // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); m_sqliteTransaction.clear(); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to open a transaction to the database"); handleTransactionError(false); return; } // Note: We intentionally retrieve the actual version even with an empty expected version. // In multi-process browsers, we take this opportinutiy to update the cached value for // the actual version. In single-process browsers, this is just a map lookup. String actualVersion; if (!m_database->getActualVersionForTransaction(actualVersion)) { m_database->disableAuthorizer(); m_sqliteTransaction.clear(); m_database->enableAuthorizer(); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version"); handleTransactionError(false); return; } m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(this)) { m_database->disableAuthorizer(); m_sqliteTransaction.clear(); m_database->enableAuthorizer(); m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); handleTransactionError(false); return; } // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object m_nextStep = &SQLTransaction::deliverTransactionCallback; LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); }
void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); // If the database was deleted, jump to the error callback if (m_database->deleted()) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); handleTransactionError(); return; } // Set the maximum usage for this transaction if this transactions is not read-only if (!m_readOnly) { acquireOriginLock(); m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); } ASSERT(!m_sqliteTransaction); m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly); m_database->resetDeletes(); m_database->disableAuthorizer(); m_sqliteTransaction->begin(); m_database->enableAuthorizer(); // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); m_sqliteTransaction = nullptr; handleTransactionError(); return; } // Note: We intentionally retrieve the actual version even with an empty expected version. // In multi-process browsers, we take this opportinutiy to update the cached value for // the actual version. In single-process browsers, this is just a map lookup. String actualVersion; if (!m_database->getActualVersionForTransaction(actualVersion)) { m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); m_database->disableAuthorizer(); m_sqliteTransaction = nullptr; m_database->enableAuthorizer(); handleTransactionError(); return; } m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(*this)) { m_database->disableAuthorizer(); m_sqliteTransaction = nullptr; m_database->enableAuthorizer(); m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight"); handleTransactionError(); return; } // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object if (m_callbackWrapper.hasCallback()) { scheduleCallback(&SQLTransaction::deliverTransactionCallback); return; } // If we have no callback to make, skip pass to the state after: runStatements(); }