michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 sts=2 et michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozStorageStatementData_h michael@0: #define mozStorageStatementData_h michael@0: michael@0: #include "sqlite3.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "MainThreadUtils.h" michael@0: michael@0: #include "mozStorageBindingParamsArray.h" michael@0: #include "mozIStorageBaseStatement.h" michael@0: #include "mozStorageConnection.h" michael@0: #include "StorageBaseStatementInternal.h" michael@0: michael@0: struct sqlite3_stmt; michael@0: michael@0: namespace mozilla { michael@0: namespace storage { michael@0: michael@0: class StatementData michael@0: { michael@0: public: michael@0: StatementData(sqlite3_stmt *aStatement, michael@0: already_AddRefed aParamsArray, michael@0: StorageBaseStatementInternal *aStatementOwner) michael@0: : mStatement(aStatement) michael@0: , mParamsArray(aParamsArray) michael@0: , mStatementOwner(aStatementOwner) michael@0: { michael@0: NS_PRECONDITION(mStatementOwner, "Must have a statement owner!"); michael@0: } michael@0: StatementData(const StatementData &aSource) michael@0: : mStatement(aSource.mStatement) michael@0: , mParamsArray(aSource.mParamsArray) michael@0: , mStatementOwner(aSource.mStatementOwner) michael@0: { michael@0: NS_PRECONDITION(mStatementOwner, "Must have a statement owner!"); michael@0: } michael@0: StatementData() michael@0: { michael@0: } michael@0: ~StatementData() michael@0: { michael@0: // We need to ensure that mParamsArray is released on the main thread, michael@0: // as the binding arguments may be XPConnect values, which are safe michael@0: // to release only on the main thread. michael@0: nsCOMPtr mainThread = do_GetMainThread(); michael@0: (void)NS_ProxyRelease(mainThread, mParamsArray); michael@0: } michael@0: michael@0: /** michael@0: * Return the sqlite statement, fetching it from the storage statement. In michael@0: * the case of AsyncStatements this may actually create the statement michael@0: */ michael@0: inline int getSqliteStatement(sqlite3_stmt **_stmt) michael@0: { michael@0: if (!mStatement) { michael@0: int rc = mStatementOwner->getAsyncStatement(&mStatement); michael@0: NS_ENSURE_TRUE(rc == SQLITE_OK, rc); michael@0: } michael@0: *_stmt = mStatement; michael@0: return SQLITE_OK; michael@0: } michael@0: michael@0: operator BindingParamsArray *() const { return mParamsArray; } michael@0: michael@0: /** michael@0: * NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and michael@0: * clear all bindings to it. This is expected to occur on the async thread. michael@0: */ michael@0: inline void reset() michael@0: { michael@0: NS_PRECONDITION(mStatementOwner, "Must have a statement owner!"); michael@0: #ifdef DEBUG michael@0: { michael@0: nsCOMPtr asyncThread = michael@0: mStatementOwner->getOwner()->getAsyncExecutionTarget(); michael@0: // It's possible that we are shutting down the async thread, and this michael@0: // method would return nullptr as a result. michael@0: if (asyncThread) { michael@0: bool onAsyncThread; michael@0: NS_ASSERTION(NS_SUCCEEDED(asyncThread->IsOnCurrentThread(&onAsyncThread)) && onAsyncThread, michael@0: "This should only be running on the async thread!"); michael@0: } michael@0: } michael@0: #endif michael@0: // In the AsyncStatement case we may never have populated mStatement if the michael@0: // AsyncExecuteStatements got canceled or a failure occurred in constructing michael@0: // the statement. michael@0: if (mStatement) { michael@0: (void)::sqlite3_reset(mStatement); michael@0: (void)::sqlite3_clear_bindings(mStatement); michael@0: mStatement = nullptr; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Indicates if this statement has parameters to be bound before it is michael@0: * executed. michael@0: * michael@0: * @return true if the statement has parameters to bind against, false michael@0: * otherwise. michael@0: */ michael@0: inline bool hasParametersToBeBound() const { return !!mParamsArray; } michael@0: /** michael@0: * Indicates the number of implicit statements generated by this statement michael@0: * requiring a transaction for execution. For example a single statement michael@0: * with N BindingParams will execute N implicit staments. michael@0: * michael@0: * @return number of statements requiring a transaction for execution. michael@0: * michael@0: * @note In the case of AsyncStatements this may actually create the michael@0: * statement. michael@0: */ michael@0: inline uint32_t needsTransaction() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: // Be sure to use the getSqliteStatement helper, since sqlite3_stmt_readonly michael@0: // can only analyze prepared statements and AsyncStatements are prepared michael@0: // lazily. michael@0: sqlite3_stmt *stmt; michael@0: int rc = getSqliteStatement(&stmt); michael@0: if (SQLITE_OK != rc || ::sqlite3_stmt_readonly(stmt)) { michael@0: return 0; michael@0: } michael@0: return mParamsArray ? mParamsArray->length() : 1; michael@0: } michael@0: michael@0: private: michael@0: sqlite3_stmt *mStatement; michael@0: nsRefPtr mParamsArray; michael@0: michael@0: /** michael@0: * We hold onto a reference of the statement's owner so it doesn't get michael@0: * destroyed out from under us. michael@0: */ michael@0: nsCOMPtr mStatementOwner; michael@0: }; michael@0: michael@0: } // namespace storage michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozStorageStatementData_h