1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/src/StorageBaseStatementInternal.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,224 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 sts=2 expandtab 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "StorageBaseStatementInternal.h" 1.11 + 1.12 +#include "nsProxyRelease.h" 1.13 + 1.14 +#include "mozStorageBindingParamsArray.h" 1.15 +#include "mozStorageStatementData.h" 1.16 +#include "mozStorageAsyncStatementExecution.h" 1.17 + 1.18 +namespace mozilla { 1.19 +namespace storage { 1.20 + 1.21 +//////////////////////////////////////////////////////////////////////////////// 1.22 +//// Local Classes 1.23 + 1.24 +/** 1.25 + * Used to finalize an asynchronous statement on the background thread. 1.26 + */ 1.27 +class AsyncStatementFinalizer : public nsRunnable 1.28 +{ 1.29 +public: 1.30 + /** 1.31 + * Constructor for the event. 1.32 + * 1.33 + * @param aStatement 1.34 + * We need the AsyncStatement to be able to get at the sqlite3_stmt; 1.35 + * we only access/create it on the async thread. 1.36 + * @param aConnection 1.37 + * We need the connection to know what thread to release the statement 1.38 + * on. We release the statement on that thread since releasing the 1.39 + * statement might end up releasing the connection too. 1.40 + */ 1.41 + AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement, 1.42 + Connection *aConnection) 1.43 + : mStatement(aStatement) 1.44 + , mConnection(aConnection) 1.45 + { 1.46 + } 1.47 + 1.48 + NS_IMETHOD Run() 1.49 + { 1.50 + if (mStatement->mAsyncStatement) { 1.51 + (void)::sqlite3_finalize(mStatement->mAsyncStatement); 1.52 + mStatement->mAsyncStatement = nullptr; 1.53 + } 1.54 + (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement); 1.55 + return NS_OK; 1.56 + } 1.57 +private: 1.58 + nsRefPtr<StorageBaseStatementInternal> mStatement; 1.59 + nsRefPtr<Connection> mConnection; 1.60 +}; 1.61 + 1.62 +/** 1.63 + * Finalize a sqlite3_stmt on the background thread for a statement whose 1.64 + * destructor was invoked and the statement was non-null. 1.65 + */ 1.66 +class LastDitchSqliteStatementFinalizer : public nsRunnable 1.67 +{ 1.68 +public: 1.69 + /** 1.70 + * Event constructor. 1.71 + * 1.72 + * @param aConnection 1.73 + * Used to keep the connection alive. If we failed to do this, it 1.74 + * is possible that the statement going out of scope invoking us 1.75 + * might have the last reference to the connection and so trigger 1.76 + * an attempt to close the connection which is doomed to fail 1.77 + * (because the asynchronous execution thread must exist which will 1.78 + * trigger the failure case). 1.79 + * @param aStatement 1.80 + * The sqlite3_stmt to finalize. This object takes ownership / 1.81 + * responsibility for the instance and all other references to it 1.82 + * should be forgotten. 1.83 + */ 1.84 + LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection, 1.85 + sqlite3_stmt *aStatement) 1.86 + : mConnection(aConnection) 1.87 + , mAsyncStatement(aStatement) 1.88 + { 1.89 + NS_PRECONDITION(aConnection, "You must provide a Connection"); 1.90 + } 1.91 + 1.92 + NS_IMETHOD Run() 1.93 + { 1.94 + (void)::sqlite3_finalize(mAsyncStatement); 1.95 + mAsyncStatement = nullptr; 1.96 + 1.97 + // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease 1.98 + // template helpers. 1.99 + Connection *rawConnection = nullptr; 1.100 + mConnection.swap(rawConnection); 1.101 + (void)::NS_ProxyRelease( 1.102 + rawConnection->threadOpenedOn, 1.103 + NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection)); 1.104 + return NS_OK; 1.105 + } 1.106 +private: 1.107 + nsRefPtr<Connection> mConnection; 1.108 + sqlite3_stmt *mAsyncStatement; 1.109 +}; 1.110 + 1.111 +//////////////////////////////////////////////////////////////////////////////// 1.112 +//// StorageBaseStatementInternal 1.113 + 1.114 +StorageBaseStatementInternal::StorageBaseStatementInternal() 1.115 +: mAsyncStatement(nullptr) 1.116 +{ 1.117 +} 1.118 + 1.119 +void 1.120 +StorageBaseStatementInternal::asyncFinalize() 1.121 +{ 1.122 + nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); 1.123 + if (target) { 1.124 + // Attempt to finalize asynchronously 1.125 + nsCOMPtr<nsIRunnable> event = 1.126 + new AsyncStatementFinalizer(this, mDBConnection); 1.127 + 1.128 + // Dispatch. Note that dispatching can fail, typically if 1.129 + // we have a race condition with asyncClose(). It's ok, 1.130 + // let asyncClose() win. 1.131 + (void)target->Dispatch(event, NS_DISPATCH_NORMAL); 1.132 + } 1.133 + // If we cannot get the background thread, 1.134 + // mozStorageConnection::AsyncClose() has already been called and 1.135 + // the statement either has been or will be cleaned up by 1.136 + // internalClose(). 1.137 +} 1.138 + 1.139 +void 1.140 +StorageBaseStatementInternal::destructorAsyncFinalize() 1.141 +{ 1.142 + if (!mAsyncStatement) 1.143 + return; 1.144 + 1.145 + // If we reach this point, our owner has not finalized this 1.146 + // statement, yet we are being destructed. If possible, we want to 1.147 + // auto-finalize it early, to release the resources early. 1.148 + nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); 1.149 + if (target) { 1.150 + // If we can get the async execution target, we can indeed finalize 1.151 + // the statement, as the connection is still open. 1.152 + bool isAsyncThread = false; 1.153 + (void)target->IsOnCurrentThread(&isAsyncThread); 1.154 + 1.155 + nsCOMPtr<nsIRunnable> event = 1.156 + new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement); 1.157 + if (isAsyncThread) { 1.158 + (void)event->Run(); 1.159 + } else { 1.160 + (void)target->Dispatch(event, NS_DISPATCH_NORMAL); 1.161 + } 1.162 + } 1.163 + 1.164 + // We might not be able to dispatch to the background thread, 1.165 + // presumably because it is being shutdown. Since said shutdown will 1.166 + // finalize the statement, we just need to clean-up around here. 1.167 + mAsyncStatement = nullptr; 1.168 +} 1.169 + 1.170 +NS_IMETHODIMP 1.171 +StorageBaseStatementInternal::NewBindingParamsArray( 1.172 + mozIStorageBindingParamsArray **_array 1.173 +) 1.174 +{ 1.175 + nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this); 1.176 + NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); 1.177 + 1.178 + array.forget(_array); 1.179 + return NS_OK; 1.180 +} 1.181 + 1.182 +NS_IMETHODIMP 1.183 +StorageBaseStatementInternal::ExecuteAsync( 1.184 + mozIStorageStatementCallback *aCallback, 1.185 + mozIStoragePendingStatement **_stmt 1.186 +) 1.187 +{ 1.188 + // We used to call Connection::ExecuteAsync but it takes a 1.189 + // mozIStorageBaseStatement signature because it is also a public API. Since 1.190 + // our 'this' has no static concept of mozIStorageBaseStatement and Connection 1.191 + // would just QI it back across to a StorageBaseStatementInternal and the 1.192 + // actual logic is very simple, we now roll our own. 1.193 + nsTArray<StatementData> stmts(1); 1.194 + StatementData data; 1.195 + nsresult rv = getAsynchronousStatementData(data); 1.196 + NS_ENSURE_SUCCESS(rv, rv); 1.197 + NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY); 1.198 + 1.199 + // Dispatch to the background 1.200 + return AsyncExecuteStatements::execute(stmts, mDBConnection, 1.201 + mNativeConnection, aCallback, _stmt); 1.202 +} 1.203 + 1.204 +NS_IMETHODIMP 1.205 +StorageBaseStatementInternal::EscapeStringForLIKE( 1.206 + const nsAString &aValue, 1.207 + const char16_t aEscapeChar, 1.208 + nsAString &_escapedString 1.209 +) 1.210 +{ 1.211 + const char16_t MATCH_ALL('%'); 1.212 + const char16_t MATCH_ONE('_'); 1.213 + 1.214 + _escapedString.Truncate(0); 1.215 + 1.216 + for (uint32_t i = 0; i < aValue.Length(); i++) { 1.217 + if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL || 1.218 + aValue[i] == MATCH_ONE) { 1.219 + _escapedString += aEscapeChar; 1.220 + } 1.221 + _escapedString += aValue[i]; 1.222 + } 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +} // namespace storage 1.227 +} // namespace mozilla