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/. */
7 #include "StorageBaseStatementInternal.h"
9 #include "nsProxyRelease.h"
11 #include "mozStorageBindingParamsArray.h"
12 #include "mozStorageStatementData.h"
13 #include "mozStorageAsyncStatementExecution.h"
18 ////////////////////////////////////////////////////////////////////////////////
22 * Used to finalize an asynchronous statement on the background thread.
24 class AsyncStatementFinalizer
: public Runnable
28 * Constructor for the event.
31 * We need the AsyncStatement to be able to get at the sqlite3_stmt;
32 * we only access/create it on the async thread.
34 * We need the connection to know what thread to release the statement
35 * on. We release the statement on that thread since releasing the
36 * statement might end up releasing the connection too.
38 AsyncStatementFinalizer(StorageBaseStatementInternal
* aStatement
,
39 Connection
* aConnection
)
40 : Runnable("storage::AsyncStatementFinalizer")
41 , mStatement(aStatement
)
42 , mConnection(aConnection
)
46 NS_IMETHOD
Run() override
48 if (mStatement
->mAsyncStatement
) {
49 sqlite3_finalize(mStatement
->mAsyncStatement
);
50 mStatement
->mAsyncStatement
= nullptr;
53 nsCOMPtr
<nsIThread
> targetThread(mConnection
->threadOpenedOn
);
55 "AsyncStatementFinalizer::mStatement", targetThread
, mStatement
.forget());
59 RefPtr
<StorageBaseStatementInternal
> mStatement
;
60 RefPtr
<Connection
> mConnection
;
64 * Finalize a sqlite3_stmt on the background thread for a statement whose
65 * destructor was invoked and the statement was non-null.
67 class LastDitchSqliteStatementFinalizer
: public Runnable
74 * Used to keep the connection alive. If we failed to do this, it
75 * is possible that the statement going out of scope invoking us
76 * might have the last reference to the connection and so trigger
77 * an attempt to close the connection which is doomed to fail
78 * (because the asynchronous execution thread must exist which will
79 * trigger the failure case).
81 * The sqlite3_stmt to finalize. This object takes ownership /
82 * responsibility for the instance and all other references to it
83 * should be forgotten.
85 LastDitchSqliteStatementFinalizer(RefPtr
<Connection
>& aConnection
,
86 sqlite3_stmt
* aStatement
)
87 : Runnable("storage::LastDitchSqliteStatementFinalizer")
88 , mConnection(aConnection
)
89 , mAsyncStatement(aStatement
)
91 MOZ_ASSERT(aConnection
, "You must provide a Connection");
94 NS_IMETHOD
Run() override
96 (void)::sqlite3_finalize(mAsyncStatement
);
97 mAsyncStatement
= nullptr;
99 nsCOMPtr
<nsIThread
> target(mConnection
->threadOpenedOn
);
100 (void)::NS_ProxyRelease(
101 "LastDitchSqliteStatementFinalizer::mConnection",
102 target
, mConnection
.forget());
106 RefPtr
<Connection
> mConnection
;
107 sqlite3_stmt
*mAsyncStatement
;
110 ////////////////////////////////////////////////////////////////////////////////
111 //// StorageBaseStatementInternal
113 StorageBaseStatementInternal::StorageBaseStatementInternal()
114 : mNativeConnection(nullptr)
115 , mAsyncStatement(nullptr)
120 StorageBaseStatementInternal::asyncFinalize()
122 nsIEventTarget
*target
= mDBConnection
->getAsyncExecutionTarget();
124 // Attempt to finalize asynchronously
125 nsCOMPtr
<nsIRunnable
> event
=
126 new AsyncStatementFinalizer(this, mDBConnection
);
128 // Dispatch. Note that dispatching can fail, typically if
129 // we have a race condition with asyncClose(). It's ok,
130 // let asyncClose() win.
131 (void)target
->Dispatch(event
, NS_DISPATCH_NORMAL
);
133 // If we cannot get the background thread,
134 // mozStorageConnection::AsyncClose() has already been called and
135 // the statement either has been or will be cleaned up by
140 StorageBaseStatementInternal::destructorAsyncFinalize()
142 if (!mAsyncStatement
)
145 bool isOwningThread
= false;
146 (void)mDBConnection
->threadOpenedOn
->IsOnCurrentThread(&isOwningThread
);
147 if (isOwningThread
) {
148 // If we are the owning thread (currently that means we're also the
149 // main thread), then we can get the async target and just dispatch
151 nsIEventTarget
*target
= mDBConnection
->getAsyncExecutionTarget();
153 nsCOMPtr
<nsIRunnable
> event
=
154 new LastDitchSqliteStatementFinalizer(mDBConnection
, mAsyncStatement
);
155 (void)target
->Dispatch(event
, NS_DISPATCH_NORMAL
);
158 // If we're not the owning thread, assume we're the async thread, and
159 // just run the statement.
160 nsCOMPtr
<nsIRunnable
> event
=
161 new LastDitchSqliteStatementFinalizer(mDBConnection
, mAsyncStatement
);
166 // We might not be able to dispatch to the background thread,
167 // presumably because it is being shutdown. Since said shutdown will
168 // finalize the statement, we just need to clean-up around here.
169 mAsyncStatement
= nullptr;
173 StorageBaseStatementInternal::NewBindingParamsArray(
174 mozIStorageBindingParamsArray
**_array
177 nsCOMPtr
<mozIStorageBindingParamsArray
> array
= new BindingParamsArray(this);
178 NS_ENSURE_TRUE(array
, NS_ERROR_OUT_OF_MEMORY
);
180 array
.forget(_array
);
185 StorageBaseStatementInternal::ExecuteAsync(
186 mozIStorageStatementCallback
*aCallback
,
187 mozIStoragePendingStatement
**_stmt
190 // We used to call Connection::ExecuteAsync but it takes a
191 // mozIStorageBaseStatement signature because it is also a public API. Since
192 // our 'this' has no static concept of mozIStorageBaseStatement and Connection
193 // would just QI it back across to a StorageBaseStatementInternal and the
194 // actual logic is very simple, we now roll our own.
195 nsTArray
<StatementData
> stmts(1);
197 nsresult rv
= getAsynchronousStatementData(data
);
198 NS_ENSURE_SUCCESS(rv
, rv
);
199 NS_ENSURE_TRUE(stmts
.AppendElement(data
), NS_ERROR_OUT_OF_MEMORY
);
201 // Dispatch to the background
202 return AsyncExecuteStatements::execute(stmts
, mDBConnection
,
203 mNativeConnection
, aCallback
, _stmt
);
207 StorageBaseStatementInternal::EscapeStringForLIKE(
208 const nsAString
&aValue
,
209 const char16_t aEscapeChar
,
210 nsAString
&_escapedString
213 const char16_t
MATCH_ALL('%');
214 const char16_t
MATCH_ONE('_');
216 _escapedString
.Truncate(0);
218 for (uint32_t i
= 0; i
< aValue
.Length(); i
++) {
219 if (aValue
[i
] == aEscapeChar
|| aValue
[i
] == MATCH_ALL
||
220 aValue
[i
] == MATCH_ONE
) {
221 _escapedString
+= aEscapeChar
;
223 _escapedString
+= aValue
[i
];
228 } // namespace storage
229 } // namespace mozilla