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