storage/src/StorageBaseStatementInternal.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim: sw=2 ts=2 sts=2 expandtab
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "StorageBaseStatementInternal.h"
michael@0 8
michael@0 9 #include "nsProxyRelease.h"
michael@0 10
michael@0 11 #include "mozStorageBindingParamsArray.h"
michael@0 12 #include "mozStorageStatementData.h"
michael@0 13 #include "mozStorageAsyncStatementExecution.h"
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16 namespace storage {
michael@0 17
michael@0 18 ////////////////////////////////////////////////////////////////////////////////
michael@0 19 //// Local Classes
michael@0 20
michael@0 21 /**
michael@0 22 * Used to finalize an asynchronous statement on the background thread.
michael@0 23 */
michael@0 24 class AsyncStatementFinalizer : public nsRunnable
michael@0 25 {
michael@0 26 public:
michael@0 27 /**
michael@0 28 * Constructor for the event.
michael@0 29 *
michael@0 30 * @param aStatement
michael@0 31 * We need the AsyncStatement to be able to get at the sqlite3_stmt;
michael@0 32 * we only access/create it on the async thread.
michael@0 33 * @param aConnection
michael@0 34 * We need the connection to know what thread to release the statement
michael@0 35 * on. We release the statement on that thread since releasing the
michael@0 36 * statement might end up releasing the connection too.
michael@0 37 */
michael@0 38 AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement,
michael@0 39 Connection *aConnection)
michael@0 40 : mStatement(aStatement)
michael@0 41 , mConnection(aConnection)
michael@0 42 {
michael@0 43 }
michael@0 44
michael@0 45 NS_IMETHOD Run()
michael@0 46 {
michael@0 47 if (mStatement->mAsyncStatement) {
michael@0 48 (void)::sqlite3_finalize(mStatement->mAsyncStatement);
michael@0 49 mStatement->mAsyncStatement = nullptr;
michael@0 50 }
michael@0 51 (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
michael@0 52 return NS_OK;
michael@0 53 }
michael@0 54 private:
michael@0 55 nsRefPtr<StorageBaseStatementInternal> mStatement;
michael@0 56 nsRefPtr<Connection> mConnection;
michael@0 57 };
michael@0 58
michael@0 59 /**
michael@0 60 * Finalize a sqlite3_stmt on the background thread for a statement whose
michael@0 61 * destructor was invoked and the statement was non-null.
michael@0 62 */
michael@0 63 class LastDitchSqliteStatementFinalizer : public nsRunnable
michael@0 64 {
michael@0 65 public:
michael@0 66 /**
michael@0 67 * Event constructor.
michael@0 68 *
michael@0 69 * @param aConnection
michael@0 70 * Used to keep the connection alive. If we failed to do this, it
michael@0 71 * is possible that the statement going out of scope invoking us
michael@0 72 * might have the last reference to the connection and so trigger
michael@0 73 * an attempt to close the connection which is doomed to fail
michael@0 74 * (because the asynchronous execution thread must exist which will
michael@0 75 * trigger the failure case).
michael@0 76 * @param aStatement
michael@0 77 * The sqlite3_stmt to finalize. This object takes ownership /
michael@0 78 * responsibility for the instance and all other references to it
michael@0 79 * should be forgotten.
michael@0 80 */
michael@0 81 LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection,
michael@0 82 sqlite3_stmt *aStatement)
michael@0 83 : mConnection(aConnection)
michael@0 84 , mAsyncStatement(aStatement)
michael@0 85 {
michael@0 86 NS_PRECONDITION(aConnection, "You must provide a Connection");
michael@0 87 }
michael@0 88
michael@0 89 NS_IMETHOD Run()
michael@0 90 {
michael@0 91 (void)::sqlite3_finalize(mAsyncStatement);
michael@0 92 mAsyncStatement = nullptr;
michael@0 93
michael@0 94 // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease
michael@0 95 // template helpers.
michael@0 96 Connection *rawConnection = nullptr;
michael@0 97 mConnection.swap(rawConnection);
michael@0 98 (void)::NS_ProxyRelease(
michael@0 99 rawConnection->threadOpenedOn,
michael@0 100 NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection));
michael@0 101 return NS_OK;
michael@0 102 }
michael@0 103 private:
michael@0 104 nsRefPtr<Connection> mConnection;
michael@0 105 sqlite3_stmt *mAsyncStatement;
michael@0 106 };
michael@0 107
michael@0 108 ////////////////////////////////////////////////////////////////////////////////
michael@0 109 //// StorageBaseStatementInternal
michael@0 110
michael@0 111 StorageBaseStatementInternal::StorageBaseStatementInternal()
michael@0 112 : mAsyncStatement(nullptr)
michael@0 113 {
michael@0 114 }
michael@0 115
michael@0 116 void
michael@0 117 StorageBaseStatementInternal::asyncFinalize()
michael@0 118 {
michael@0 119 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
michael@0 120 if (target) {
michael@0 121 // Attempt to finalize asynchronously
michael@0 122 nsCOMPtr<nsIRunnable> event =
michael@0 123 new AsyncStatementFinalizer(this, mDBConnection);
michael@0 124
michael@0 125 // Dispatch. Note that dispatching can fail, typically if
michael@0 126 // we have a race condition with asyncClose(). It's ok,
michael@0 127 // let asyncClose() win.
michael@0 128 (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 129 }
michael@0 130 // If we cannot get the background thread,
michael@0 131 // mozStorageConnection::AsyncClose() has already been called and
michael@0 132 // the statement either has been or will be cleaned up by
michael@0 133 // internalClose().
michael@0 134 }
michael@0 135
michael@0 136 void
michael@0 137 StorageBaseStatementInternal::destructorAsyncFinalize()
michael@0 138 {
michael@0 139 if (!mAsyncStatement)
michael@0 140 return;
michael@0 141
michael@0 142 // If we reach this point, our owner has not finalized this
michael@0 143 // statement, yet we are being destructed. If possible, we want to
michael@0 144 // auto-finalize it early, to release the resources early.
michael@0 145 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
michael@0 146 if (target) {
michael@0 147 // If we can get the async execution target, we can indeed finalize
michael@0 148 // the statement, as the connection is still open.
michael@0 149 bool isAsyncThread = false;
michael@0 150 (void)target->IsOnCurrentThread(&isAsyncThread);
michael@0 151
michael@0 152 nsCOMPtr<nsIRunnable> event =
michael@0 153 new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
michael@0 154 if (isAsyncThread) {
michael@0 155 (void)event->Run();
michael@0 156 } else {
michael@0 157 (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 158 }
michael@0 159 }
michael@0 160
michael@0 161 // We might not be able to dispatch to the background thread,
michael@0 162 // presumably because it is being shutdown. Since said shutdown will
michael@0 163 // finalize the statement, we just need to clean-up around here.
michael@0 164 mAsyncStatement = nullptr;
michael@0 165 }
michael@0 166
michael@0 167 NS_IMETHODIMP
michael@0 168 StorageBaseStatementInternal::NewBindingParamsArray(
michael@0 169 mozIStorageBindingParamsArray **_array
michael@0 170 )
michael@0 171 {
michael@0 172 nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
michael@0 173 NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
michael@0 174
michael@0 175 array.forget(_array);
michael@0 176 return NS_OK;
michael@0 177 }
michael@0 178
michael@0 179 NS_IMETHODIMP
michael@0 180 StorageBaseStatementInternal::ExecuteAsync(
michael@0 181 mozIStorageStatementCallback *aCallback,
michael@0 182 mozIStoragePendingStatement **_stmt
michael@0 183 )
michael@0 184 {
michael@0 185 // We used to call Connection::ExecuteAsync but it takes a
michael@0 186 // mozIStorageBaseStatement signature because it is also a public API. Since
michael@0 187 // our 'this' has no static concept of mozIStorageBaseStatement and Connection
michael@0 188 // would just QI it back across to a StorageBaseStatementInternal and the
michael@0 189 // actual logic is very simple, we now roll our own.
michael@0 190 nsTArray<StatementData> stmts(1);
michael@0 191 StatementData data;
michael@0 192 nsresult rv = getAsynchronousStatementData(data);
michael@0 193 NS_ENSURE_SUCCESS(rv, rv);
michael@0 194 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
michael@0 195
michael@0 196 // Dispatch to the background
michael@0 197 return AsyncExecuteStatements::execute(stmts, mDBConnection,
michael@0 198 mNativeConnection, aCallback, _stmt);
michael@0 199 }
michael@0 200
michael@0 201 NS_IMETHODIMP
michael@0 202 StorageBaseStatementInternal::EscapeStringForLIKE(
michael@0 203 const nsAString &aValue,
michael@0 204 const char16_t aEscapeChar,
michael@0 205 nsAString &_escapedString
michael@0 206 )
michael@0 207 {
michael@0 208 const char16_t MATCH_ALL('%');
michael@0 209 const char16_t MATCH_ONE('_');
michael@0 210
michael@0 211 _escapedString.Truncate(0);
michael@0 212
michael@0 213 for (uint32_t i = 0; i < aValue.Length(); i++) {
michael@0 214 if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
michael@0 215 aValue[i] == MATCH_ONE) {
michael@0 216 _escapedString += aEscapeChar;
michael@0 217 }
michael@0 218 _escapedString += aValue[i];
michael@0 219 }
michael@0 220 return NS_OK;
michael@0 221 }
michael@0 222
michael@0 223 } // namespace storage
michael@0 224 } // namespace mozilla

mercurial