chromium/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.cc

/*
 * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.h"

#include <memory>

#include "third_party/blink/renderer/modules/webdatabase/database.h"
#include "third_party/blink/renderer/modules/webdatabase/database_authorizer.h"
#include "third_party/blink/renderer/modules/webdatabase/database_context.h"
#include "third_party/blink/renderer/modules/webdatabase/database_thread.h"
#include "third_party/blink/renderer/modules/webdatabase/database_tracker.h"
#include "third_party/blink/renderer/modules/webdatabase/sql_error.h"
#include "third_party/blink/renderer/modules/webdatabase/sql_statement_backend.h"
#include "third_party/blink/renderer/modules/webdatabase/sql_transaction.h"
#include "third_party/blink/renderer/modules/webdatabase/sql_transaction_client.h"
#include "third_party/blink/renderer/modules/webdatabase/sql_transaction_coordinator.h"
#include "third_party/blink/renderer/modules/webdatabase/sqlite/sql_value.h"
#include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_transaction.h"
#include "third_party/blink/renderer/modules/webdatabase/storage_log.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"

// How does a SQLTransaction work?
// ==============================
// The SQLTransaction is a state machine that executes a series of states /
// steps.
//
// The work of the transaction states are defined in section of 4.3.2 of the
// webdatabase spec: http://dev.w3.org/html5/webdatabase/#processing-model
//
// the State Transition Graph at a glance:
// ======================================
//
//     Backend                        .   Frontend
//     (works with SQLiteDatabase)    .   (works with Script)
//     ===========================    .   ===================
//                                    .
//     1. Idle                        .
//         v                          .
//     2. AcquireLock                 .
//         v                          .
//     3. OpenTransactionAndPreflight -----------------------------------.
//         |                        .                                    |
//         `-------------------------> 8. DeliverTransactionCallback --. |
//                                  .     |                           v v
//         ,------------------------------' 9. DeliverTransactionErrorCallback +
//         |                        .                                  ^ ^ ^   |
//         v                        .                                  | | |   |
//     4. RunStatements -----------------------------------------------' | |   |
//         |        ^  ^ |  ^ |     .                                    | |   |
//         |--------'  | |  | `------> 10. DeliverStatementCallback +----' |   |
//         |           | |  `---------------------------------------'      |   |
//         |           | `-----------> 11. DeliverQuotaIncreaseCallback +  |   |
//         |            `-----------------------------------------------'  |   |
//         v                        .                                      |   |
//     5. PostflightAndCommit --+------------------------------------------'   |
//                              |----> 12. DeliverSuccessCallback +            |
//         ,--------------------'   .                             |            |
//         v                        .                             |            |
//     6. CleanupAndTerminate <-----------------------------------'            |
//         v           ^            .                                          |
//     0. End          |            .                                          |
//                     |            .                                          |
//                7: CleanupAfterTransactionErrorCallback <--------------------'
//                                  .
//
// the States and State Transitions:
// ================================
//     0. SQLTransactionState::End
//         - the end state.
//
//     1. SQLTransactionState::Idle
//         - placeholder state while waiting on frontend/backend, etc. See
//           comment on "State transitions between SQLTransaction and
//           SQLTransactionBackend" below.
//
//     2. SQLTransactionState::AcquireLock (runs in backend)
//         - this is the start state.
//         - acquire the "lock".
//         - on "lock" acquisition, goto
//           SQLTransactionState::OpenTransactionAndPreflight.
//
//     3. SQLTransactionState::openTransactionAndPreflight (runs in backend)
//         - Sets up an SQLiteTransaction.
//         - begin the SQLiteTransaction.
//         - call the SQLTransactionWrapper preflight if available.
//         - schedule script callback.
//         - on error, goto
//           SQLTransactionState::DeliverTransactionErrorCallback.
//         - goto SQLTransactionState::DeliverTransactionCallback.
//
//     4. SQLTransactionState::DeliverTransactionCallback (runs in frontend)
//         - invoke the script function callback() if available.
//         - on error, goto
//           SQLTransactionState::DeliverTransactionErrorCallback.
//         - goto SQLTransactionState::RunStatements.
//
//     5. SQLTransactionState::DeliverTransactionErrorCallback (runs in
//        frontend)
//         - invoke the script function errorCallback if available.
//         - goto SQLTransactionState::CleanupAfterTransactionErrorCallback.
//
//     6. SQLTransactionState::RunStatements (runs in backend)
//         - while there are statements {
//             - run a statement.
//             - if statementCallback is available, goto
//               SQLTransactionState::DeliverStatementCallback.
//             - on error,
//               goto SQLTransactionState::DeliverQuotaIncreaseCallback, or
//               goto SQLTransactionState::DeliverStatementCallback, or
//               goto SQLTransactionState::deliverTransactionErrorCallback.
//           }
//         - goto SQLTransactionState::PostflightAndCommit.
//
//     7. SQLTransactionState::DeliverStatementCallback (runs in frontend)
//         - invoke script statement callback (assume available).
//         - on error, goto
//           SQLTransactionState::DeliverTransactionErrorCallback.
//         - goto SQLTransactionState::RunStatements.
//
//     8. SQLTransactionState::DeliverQuotaIncreaseCallback (runs in frontend)
//         - give client a chance to increase the quota.
//         - goto SQLTransactionState::RunStatements.
//
//     9. SQLTransactionState::PostflightAndCommit (runs in backend)
//         - call the SQLTransactionWrapper postflight if available.
//         - commit the SQLiteTansaction.
//         - on error, goto
//           SQLTransactionState::DeliverTransactionErrorCallback.
//         - if successCallback is available, goto
//           SQLTransactionState::DeliverSuccessCallback.
//           else goto SQLTransactionState::CleanupAndTerminate.
//
//     10. SQLTransactionState::DeliverSuccessCallback (runs in frontend)
//         - invoke the script function successCallback() if available.
//         - goto SQLTransactionState::CleanupAndTerminate.
//
//     11. SQLTransactionState::CleanupAndTerminate (runs in backend)
//         - stop and clear the SQLiteTransaction.
//         - release the "lock".
//         - goto SQLTransactionState::End.
//
//     12. SQLTransactionState::CleanupAfterTransactionErrorCallback (runs in
//         backend)
//         - rollback the SQLiteTransaction.
//         - goto SQLTransactionState::CleanupAndTerminate.
//
// State transitions between SQLTransaction and SQLTransactionBackend
// ==================================================================
// As shown above, there are state transitions that crosses the boundary between
// the frontend and backend. For example,
//
//     OpenTransactionAndPreflight (state 3 in the backend)
//     transitions to DeliverTransactionCallback (state 8 in the frontend),
//     which in turn transitions to RunStatements (state 4 in the backend).
//
// This cross boundary transition is done by posting transition requests to the
// other side and letting the other side's state machine execute the state
// transition in the appropriate thread (i.e. the script thread for the
// frontend, and the database thread for the backend).
//
// Logically, the state transitions work as shown in the graph above. But
// physically, the transition mechanism uses the Idle state (both in the
// frontend and backend) as a waiting state for further activity. For example,
// taking a closer look at the 3 state transition example above, what actually
// happens is as follows:
//
//     Step 1:
//     ======
//     In the frontend thread:
//     - waiting quietly is Idle. Not doing any work.
//
//     In the backend:
//     - is in OpenTransactionAndPreflight, and doing its work.
//     - when done, it transits to the backend DeliverTransactionCallback.
//     - the backend DeliverTransactionCallback sends a request to the frontend
//       to transit to DeliverTransactionCallback, and then itself transits to
//       Idle.
//
//     Step 2:
//     ======
//     In the frontend thread:
//     - transits to DeliverTransactionCallback and does its work.
//     - when done, it transits to the frontend RunStatements.
//     - the frontend RunStatements sends a request to the backend to transit
//       to RunStatements, and then itself transits to Idle.
//
//     In the backend:
//     - waiting quietly in Idle.
//
//     Step 3:
//     ======
//     In the frontend thread:
//     - waiting quietly is Idle. Not doing any work.
//
//     In the backend:
//     - transits to RunStatements, and does its work.
//        ...
//
// So, when the frontend or backend are not active, they will park themselves in
// their Idle states. This means their m_nextState is set to Idle, but they
// never actually run the corresponding state function. Note: for both the
// frontend and backend, the state function for Idle is unreachableState().
//
// The states that send a request to their peer across the front/back boundary
// are implemented with just 2 functions: SQLTransaction::sendToBackendState()
// and SQLTransactionBackend::sendToFrontendState(). These state functions do
// nothing but sends a request to the other side to transit to the current
// state (indicated by m_nextState), and then transits itself to the Idle state
// to wait for further action.

// The Life-Cycle of a SQLTransaction i.e. Who's keeping the SQLTransaction
// alive?
// ==============================================================================
// The RefPtr chain goes something like this:
//
//     At birth (in Database::runTransaction()):
//     ====================================================
//     Database
//         // HeapDeque<Member<SQLTransactionBackend>> m_transactionQueue
//         // points to ...
//     --> SQLTransactionBackend
//         // Member<SQLTransaction> m_frontend points to ...
//     --> SQLTransaction
//         // Member<SQLTransactionBackend> m_backend points to ...
//     --> SQLTransactionBackend  // which is a circular reference.
//
//     Note: there's a circular reference between the SQLTransaction front-end
//     and back-end. This circular reference is established in the constructor
//     of the SQLTransactionBackend. The circular reference will be broken by
//     calling doCleanup() to nullify m_frontend. This is done at the end of the
//     transaction's clean up state (i.e. when the transaction should no longer
//     be in use thereafter), or if the database was interrupted. See comments
//     on "What happens if a transaction is interrupted?" below for details.
//
//     After scheduling the transaction with the DatabaseThread
//     (Database::scheduleTransaction()):
//     ======================================================================================================
//     DatabaseThread
//         // MessageQueue<DatabaseTask> m_queue points to ...
//     --> DatabaseTransactionTask
//         // Member<SQLTransactionBackend> m_transaction points to ...
//     --> SQLTransactionBackend
//         // Member<SQLTransaction> m_frontend points to ...
//     --> SQLTransaction
//         // Member<SQLTransactionBackend> m_backend points to ...
//     --> SQLTransactionBackend  // which is a circular reference.
//
//     When executing the transaction (in DatabaseThread::databaseThread()):
//     ====================================================================
//     std::unique_ptr<DatabaseTask> task;
//         // points to ...
//     --> DatabaseTransactionTask
//         // Member<SQLTransactionBackend> m_transaction points to ...
//     --> SQLTransactionBackend
//         // Member<SQLTransaction> m_frontend;
//     --> SQLTransaction
//         // Member<SQLTransactionBackend> m_backend points to ...
//     --> SQLTransactionBackend  // which is a circular reference.
//
//     At the end of cleanupAndTerminate():
//     ===================================
//     At the end of the cleanup state, the SQLTransactionBackend::m_frontend is
//     nullified.  If by then, a JSObject wrapper is referring to the
//     SQLTransaction, then the reference chain looks like this:
//
//     JSObjectWrapper
//     --> SQLTransaction
//         // in Member<SQLTransactionBackend> m_backend points to ...
//     --> SQLTransactionBackend
//         // which no longer points back to its SQLTransaction.
//
//     When the GC collects the corresponding JSObject, the above chain will be
//     cleaned up and deleted.
//
//     If there is no JSObject wrapper referring to the SQLTransaction when the
//     cleanup states nullify SQLTransactionBackend::m_frontend, the
//     SQLTransaction will deleted then.  However, there will still be a
//     DatabaseTask pointing to the SQLTransactionBackend (see the "When
//     executing the transaction" chain above). This will keep the
//     SQLTransactionBackend alive until DatabaseThread::databaseThread()
//     releases its task std::unique_ptr.
//
//     What happens if a transaction is interrupted?
//     ============================================
//     If the transaction is interrupted half way, it won't get to run to state
//     CleanupAndTerminate, and hence, would not have called
//     SQLTransactionBackend's doCleanup(). doCleanup() is where we nullify
//     SQLTransactionBackend::m_frontend to break the reference cycle between
//     the frontend and backend. Hence, we need to cleanup the transaction by
//     other means.
//
//     Note: calling SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown()
//     is effectively the same as calling SQLTransactionBackend::doClean().
//
//     In terms of who needs to call doCleanup(), there are 5 phases in the
//     SQLTransactionBackend life-cycle. These are the phases and how the clean
//     up is done:
//
//     Phase 1. After Birth, before scheduling
//
//     - To clean up, DatabaseThread::databaseThread() will call
//       Database::close() during its shutdown.
//     - Database::close() will iterate
//       Database::m_transactionQueue and call
//       notifyDatabaseThreadIsShuttingDown() on each transaction there.
//
//     Phase 2. After scheduling, before state AcquireLock
//
//     - If the interruption occures before the DatabaseTransactionTask is
//       scheduled in DatabaseThread::m_queue but hasn't gotten to execute
//       (i.e. DatabaseTransactionTask::performTask() has not been called),
//       then the DatabaseTransactionTask may get destructed before it ever
//       gets to execute.
//     - To clean up, the destructor will check if the task's m_wasExecuted is
//       set. If not, it will call notifyDatabaseThreadIsShuttingDown() on
//       the task's transaction.
//
//     Phase 3. After state AcquireLock, before "lockAcquired"
//
//     - In this phase, the transaction would have been added to the
//       SQLTransactionCoordinator's CoordinationInfo's pendingTransactions.
//     - To clean up, during shutdown, DatabaseThread::databaseThread() calls
//       SQLTransactionCoordinator::shutdown(), which calls
//       notifyDatabaseThreadIsShuttingDown().
//
//     Phase 4: After "lockAcquired", before state CleanupAndTerminate
//
//     - In this phase, the transaction would have been added either to the
//       SQLTransactionCoordinator's CoordinationInfo's activeWriteTransaction
//       or activeReadTransactions.
//     - To clean up, during shutdown, DatabaseThread::databaseThread() calls
//       SQLTransactionCoordinator::shutdown(), which calls
//       notifyDatabaseThreadIsShuttingDown().
//
//     Phase 5: After state CleanupAndTerminate
//
//     - This is how a transaction ends normally.
//     - state CleanupAndTerminate calls doCleanup().

namespace blink {

SQLTransactionBackend::SQLTransactionBackend(Database* db,
                                             SQLTransaction* frontend,
                                             SQLTransactionWrapper* wrapper,
                                             bool read_only)
    :{}

SQLTransactionBackend::~SQLTransactionBackend() {}

void SQLTransactionBackend::Trace(Visitor* visitor) const {}

void SQLTransactionBackend::DoCleanup() {}

SQLStatement* SQLTransactionBackend::CurrentStatement() {}

SQLErrorData* SQLTransactionBackend::TransactionError() {}

void SQLTransactionBackend::SetShouldRetryCurrentStatement(bool should_retry) {}

SQLTransactionBackend::StateFunction SQLTransactionBackend::StateFunctionFor(
    SQLTransactionState state) {}

void SQLTransactionBackend::EnqueueStatementBackend(
    SQLStatementBackend* statement_backend) {}

void SQLTransactionBackend::ComputeNextStateAndCleanupIfNeeded() {}

void SQLTransactionBackend::PerformNextStep() {}

void SQLTransactionBackend::ExecuteSQL(SQLStatement* statement,
                                       const String& sql_statement,
                                       const Vector<SQLValue>& arguments,
                                       int permissions) {}

void SQLTransactionBackend::NotifyDatabaseThreadIsShuttingDown() {}

SQLTransactionState SQLTransactionBackend::AcquireLock() {}

void SQLTransactionBackend::LockAcquired() {}

SQLTransactionState SQLTransactionBackend::OpenTransactionAndPreflight() {}

SQLTransactionState SQLTransactionBackend::RunStatements() {}

void SQLTransactionBackend::GetNextStatement() {}

SQLTransactionState
SQLTransactionBackend::RunCurrentStatementAndGetNextState() {}

SQLTransactionState SQLTransactionBackend::NextStateForCurrentStatementError() {}

SQLTransactionState SQLTransactionBackend::PostflightAndCommit() {}

SQLTransactionState SQLTransactionBackend::CleanupAndTerminate() {}

SQLTransactionState SQLTransactionBackend::NextStateForTransactionError() {}

SQLTransactionState
SQLTransactionBackend::CleanupAfterTransactionErrorCallback() {}

// requestTransitToState() can be called from the frontend. Hence, it should
// NOT be modifying SQLTransactionBackend in general. The only safe field to
// modify is m_requestedState which is meant for this purpose.
void SQLTransactionBackend::RequestTransitToState(
    SQLTransactionState next_state) {}

// This state function is used as a stub function to plug unimplemented states
// in the state dispatch table. They are unimplemented because they should
// never be reached in the course of correct execution.
SQLTransactionState SQLTransactionBackend::UnreachableState() {}

SQLTransactionState SQLTransactionBackend::SendToFrontendState() {}

}  // namespace blink