| |
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
| |
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : |
| |
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 <stdio.h> |
| |
8 |
| |
9 #include "nsError.h" |
| |
10 #include "nsIMutableArray.h" |
| |
11 #include "nsAutoPtr.h" |
| |
12 #include "nsIMemoryReporter.h" |
| |
13 #include "nsThreadUtils.h" |
| |
14 #include "nsIFile.h" |
| |
15 #include "nsIFileURL.h" |
| |
16 #include "mozilla/Telemetry.h" |
| |
17 #include "mozilla/Mutex.h" |
| |
18 #include "mozilla/CondVar.h" |
| |
19 #include "mozilla/Attributes.h" |
| |
20 |
| |
21 #include "mozIStorageAggregateFunction.h" |
| |
22 #include "mozIStorageCompletionCallback.h" |
| |
23 #include "mozIStorageFunction.h" |
| |
24 |
| |
25 #include "mozStorageAsyncStatementExecution.h" |
| |
26 #include "mozStorageSQLFunctions.h" |
| |
27 #include "mozStorageConnection.h" |
| |
28 #include "mozStorageService.h" |
| |
29 #include "mozStorageStatement.h" |
| |
30 #include "mozStorageAsyncStatement.h" |
| |
31 #include "mozStorageArgValueArray.h" |
| |
32 #include "mozStoragePrivateHelpers.h" |
| |
33 #include "mozStorageStatementData.h" |
| |
34 #include "StorageBaseStatementInternal.h" |
| |
35 #include "SQLCollations.h" |
| |
36 #include "FileSystemModule.h" |
| |
37 #include "mozStorageHelper.h" |
| |
38 #include "GeckoProfiler.h" |
| |
39 |
| |
40 #include "prlog.h" |
| |
41 #include "prprf.h" |
| |
42 #include "nsProxyRelease.h" |
| |
43 #include <algorithm> |
| |
44 |
| |
45 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB |
| |
46 |
| |
47 // Maximum size of the pages cache per connection. |
| |
48 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB |
| |
49 |
| |
50 #ifdef PR_LOGGING |
| |
51 PRLogModuleInfo* gStorageLog = nullptr; |
| |
52 #endif |
| |
53 |
| |
54 namespace mozilla { |
| |
55 namespace storage { |
| |
56 |
| |
57 namespace { |
| |
58 |
| |
59 //////////////////////////////////////////////////////////////////////////////// |
| |
60 //// Variant Specialization Functions (variantToSQLiteT) |
| |
61 |
| |
62 int |
| |
63 sqlite3_T_int(sqlite3_context *aCtx, |
| |
64 int aValue) |
| |
65 { |
| |
66 ::sqlite3_result_int(aCtx, aValue); |
| |
67 return SQLITE_OK; |
| |
68 } |
| |
69 |
| |
70 int |
| |
71 sqlite3_T_int64(sqlite3_context *aCtx, |
| |
72 sqlite3_int64 aValue) |
| |
73 { |
| |
74 ::sqlite3_result_int64(aCtx, aValue); |
| |
75 return SQLITE_OK; |
| |
76 } |
| |
77 |
| |
78 int |
| |
79 sqlite3_T_double(sqlite3_context *aCtx, |
| |
80 double aValue) |
| |
81 { |
| |
82 ::sqlite3_result_double(aCtx, aValue); |
| |
83 return SQLITE_OK; |
| |
84 } |
| |
85 |
| |
86 int |
| |
87 sqlite3_T_text(sqlite3_context *aCtx, |
| |
88 const nsCString &aValue) |
| |
89 { |
| |
90 ::sqlite3_result_text(aCtx, |
| |
91 aValue.get(), |
| |
92 aValue.Length(), |
| |
93 SQLITE_TRANSIENT); |
| |
94 return SQLITE_OK; |
| |
95 } |
| |
96 |
| |
97 int |
| |
98 sqlite3_T_text16(sqlite3_context *aCtx, |
| |
99 const nsString &aValue) |
| |
100 { |
| |
101 ::sqlite3_result_text16(aCtx, |
| |
102 aValue.get(), |
| |
103 aValue.Length() * 2, // Number of bytes. |
| |
104 SQLITE_TRANSIENT); |
| |
105 return SQLITE_OK; |
| |
106 } |
| |
107 |
| |
108 int |
| |
109 sqlite3_T_null(sqlite3_context *aCtx) |
| |
110 { |
| |
111 ::sqlite3_result_null(aCtx); |
| |
112 return SQLITE_OK; |
| |
113 } |
| |
114 |
| |
115 int |
| |
116 sqlite3_T_blob(sqlite3_context *aCtx, |
| |
117 const void *aData, |
| |
118 int aSize) |
| |
119 { |
| |
120 ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free); |
| |
121 return SQLITE_OK; |
| |
122 } |
| |
123 |
| |
124 #include "variantToSQLiteT_impl.h" |
| |
125 |
| |
126 //////////////////////////////////////////////////////////////////////////////// |
| |
127 //// Modules |
| |
128 |
| |
129 struct Module |
| |
130 { |
| |
131 const char* name; |
| |
132 int (*registerFunc)(sqlite3*, const char*); |
| |
133 }; |
| |
134 |
| |
135 Module gModules[] = { |
| |
136 { "filesystem", RegisterFileSystemModule } |
| |
137 }; |
| |
138 |
| |
139 //////////////////////////////////////////////////////////////////////////////// |
| |
140 //// Local Functions |
| |
141 |
| |
142 #ifdef PR_LOGGING |
| |
143 void tracefunc (void *aClosure, const char *aStmt) |
| |
144 { |
| |
145 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure, |
| |
146 aStmt)); |
| |
147 } |
| |
148 #endif |
| |
149 |
| |
150 struct FFEArguments |
| |
151 { |
| |
152 nsISupports *target; |
| |
153 bool found; |
| |
154 }; |
| |
155 PLDHashOperator |
| |
156 findFunctionEnumerator(const nsACString &aKey, |
| |
157 Connection::FunctionInfo aData, |
| |
158 void *aUserArg) |
| |
159 { |
| |
160 FFEArguments *args = static_cast<FFEArguments *>(aUserArg); |
| |
161 if (aData.function == args->target) { |
| |
162 args->found = true; |
| |
163 return PL_DHASH_STOP; |
| |
164 } |
| |
165 return PL_DHASH_NEXT; |
| |
166 } |
| |
167 |
| |
168 PLDHashOperator |
| |
169 copyFunctionEnumerator(const nsACString &aKey, |
| |
170 Connection::FunctionInfo aData, |
| |
171 void *aUserArg) |
| |
172 { |
| |
173 NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE || |
| |
174 aData.type == Connection::FunctionInfo::AGGREGATE, |
| |
175 "Invalid function type!"); |
| |
176 |
| |
177 Connection *connection = static_cast<Connection *>(aUserArg); |
| |
178 if (aData.type == Connection::FunctionInfo::SIMPLE) { |
| |
179 mozIStorageFunction *function = |
| |
180 static_cast<mozIStorageFunction *>(aData.function.get()); |
| |
181 (void)connection->CreateFunction(aKey, aData.numArgs, function); |
| |
182 } |
| |
183 else { |
| |
184 mozIStorageAggregateFunction *function = |
| |
185 static_cast<mozIStorageAggregateFunction *>(aData.function.get()); |
| |
186 (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function); |
| |
187 } |
| |
188 |
| |
189 return PL_DHASH_NEXT; |
| |
190 } |
| |
191 |
| |
192 void |
| |
193 basicFunctionHelper(sqlite3_context *aCtx, |
| |
194 int aArgc, |
| |
195 sqlite3_value **aArgv) |
| |
196 { |
| |
197 void *userData = ::sqlite3_user_data(aCtx); |
| |
198 |
| |
199 mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData); |
| |
200 |
| |
201 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv)); |
| |
202 if (!arguments) |
| |
203 return; |
| |
204 |
| |
205 nsCOMPtr<nsIVariant> result; |
| |
206 if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) { |
| |
207 NS_WARNING("User function returned error code!"); |
| |
208 ::sqlite3_result_error(aCtx, |
| |
209 "User function returned error code", |
| |
210 -1); |
| |
211 return; |
| |
212 } |
| |
213 int retcode = variantToSQLiteT(aCtx, result); |
| |
214 if (retcode == SQLITE_IGNORE) { |
| |
215 ::sqlite3_result_int(aCtx, SQLITE_IGNORE); |
| |
216 } else if (retcode != SQLITE_OK) { |
| |
217 NS_WARNING("User function returned invalid data type!"); |
| |
218 ::sqlite3_result_error(aCtx, |
| |
219 "User function returned invalid data type", |
| |
220 -1); |
| |
221 } |
| |
222 } |
| |
223 |
| |
224 void |
| |
225 aggregateFunctionStepHelper(sqlite3_context *aCtx, |
| |
226 int aArgc, |
| |
227 sqlite3_value **aArgv) |
| |
228 { |
| |
229 void *userData = ::sqlite3_user_data(aCtx); |
| |
230 mozIStorageAggregateFunction *func = |
| |
231 static_cast<mozIStorageAggregateFunction *>(userData); |
| |
232 |
| |
233 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv)); |
| |
234 if (!arguments) |
| |
235 return; |
| |
236 |
| |
237 if (NS_FAILED(func->OnStep(arguments))) |
| |
238 NS_WARNING("User aggregate step function returned error code!"); |
| |
239 } |
| |
240 |
| |
241 void |
| |
242 aggregateFunctionFinalHelper(sqlite3_context *aCtx) |
| |
243 { |
| |
244 void *userData = ::sqlite3_user_data(aCtx); |
| |
245 mozIStorageAggregateFunction *func = |
| |
246 static_cast<mozIStorageAggregateFunction *>(userData); |
| |
247 |
| |
248 nsRefPtr<nsIVariant> result; |
| |
249 if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) { |
| |
250 NS_WARNING("User aggregate final function returned error code!"); |
| |
251 ::sqlite3_result_error(aCtx, |
| |
252 "User aggregate final function returned error code", |
| |
253 -1); |
| |
254 return; |
| |
255 } |
| |
256 |
| |
257 if (variantToSQLiteT(aCtx, result) != SQLITE_OK) { |
| |
258 NS_WARNING("User aggregate final function returned invalid data type!"); |
| |
259 ::sqlite3_result_error(aCtx, |
| |
260 "User aggregate final function returned invalid data type", |
| |
261 -1); |
| |
262 } |
| |
263 } |
| |
264 |
| |
265 /** |
| |
266 * This code is heavily based on the sample at: |
| |
267 * http://www.sqlite.org/unlock_notify.html |
| |
268 */ |
| |
269 class UnlockNotification |
| |
270 { |
| |
271 public: |
| |
272 UnlockNotification() |
| |
273 : mMutex("UnlockNotification mMutex") |
| |
274 , mCondVar(mMutex, "UnlockNotification condVar") |
| |
275 , mSignaled(false) |
| |
276 { |
| |
277 } |
| |
278 |
| |
279 void Wait() |
| |
280 { |
| |
281 MutexAutoLock lock(mMutex); |
| |
282 while (!mSignaled) { |
| |
283 (void)mCondVar.Wait(); |
| |
284 } |
| |
285 } |
| |
286 |
| |
287 void Signal() |
| |
288 { |
| |
289 MutexAutoLock lock(mMutex); |
| |
290 mSignaled = true; |
| |
291 (void)mCondVar.Notify(); |
| |
292 } |
| |
293 |
| |
294 private: |
| |
295 Mutex mMutex; |
| |
296 CondVar mCondVar; |
| |
297 bool mSignaled; |
| |
298 }; |
| |
299 |
| |
300 void |
| |
301 UnlockNotifyCallback(void **aArgs, |
| |
302 int aArgsSize) |
| |
303 { |
| |
304 for (int i = 0; i < aArgsSize; i++) { |
| |
305 UnlockNotification *notification = |
| |
306 static_cast<UnlockNotification *>(aArgs[i]); |
| |
307 notification->Signal(); |
| |
308 } |
| |
309 } |
| |
310 |
| |
311 int |
| |
312 WaitForUnlockNotify(sqlite3* aDatabase) |
| |
313 { |
| |
314 UnlockNotification notification; |
| |
315 int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback, |
| |
316 ¬ification); |
| |
317 MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK); |
| |
318 if (srv == SQLITE_OK) { |
| |
319 notification.Wait(); |
| |
320 } |
| |
321 |
| |
322 return srv; |
| |
323 } |
| |
324 |
| |
325 } // anonymous namespace |
| |
326 |
| |
327 //////////////////////////////////////////////////////////////////////////////// |
| |
328 //// Local Classes |
| |
329 |
| |
330 namespace { |
| |
331 |
| |
332 class AsyncCloseConnection MOZ_FINAL: public nsRunnable |
| |
333 { |
| |
334 public: |
| |
335 AsyncCloseConnection(Connection *aConnection, |
| |
336 sqlite3 *aNativeConnection, |
| |
337 nsIRunnable *aCallbackEvent, |
| |
338 already_AddRefed<nsIThread> aAsyncExecutionThread) |
| |
339 : mConnection(aConnection) |
| |
340 , mNativeConnection(aNativeConnection) |
| |
341 , mCallbackEvent(aCallbackEvent) |
| |
342 , mAsyncExecutionThread(aAsyncExecutionThread) |
| |
343 { |
| |
344 } |
| |
345 |
| |
346 NS_METHOD Run() |
| |
347 { |
| |
348 #ifdef DEBUG |
| |
349 // This code is executed on the background thread |
| |
350 bool onAsyncThread = false; |
| |
351 (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread); |
| |
352 MOZ_ASSERT(onAsyncThread); |
| |
353 #endif // DEBUG |
| |
354 |
| |
355 // Internal close. |
| |
356 (void)mConnection->internalClose(mNativeConnection); |
| |
357 |
| |
358 // Callback |
| |
359 if (mCallbackEvent) { |
| |
360 nsCOMPtr<nsIThread> thread; |
| |
361 (void)NS_GetMainThread(getter_AddRefs(thread)); |
| |
362 (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL); |
| |
363 } |
| |
364 |
| |
365 return NS_OK; |
| |
366 } |
| |
367 |
| |
368 ~AsyncCloseConnection() { |
| |
369 nsCOMPtr<nsIThread> thread; |
| |
370 (void)NS_GetMainThread(getter_AddRefs(thread)); |
| |
371 // Handle ambiguous nsISupports inheritance. |
| |
372 Connection *rawConnection = nullptr; |
| |
373 mConnection.swap(rawConnection); |
| |
374 (void)NS_ProxyRelease(thread, |
| |
375 NS_ISUPPORTS_CAST(mozIStorageConnection *, |
| |
376 rawConnection)); |
| |
377 (void)NS_ProxyRelease(thread, mCallbackEvent); |
| |
378 } |
| |
379 private: |
| |
380 nsRefPtr<Connection> mConnection; |
| |
381 sqlite3 *mNativeConnection; |
| |
382 nsCOMPtr<nsIRunnable> mCallbackEvent; |
| |
383 nsCOMPtr<nsIThread> mAsyncExecutionThread; |
| |
384 }; |
| |
385 |
| |
386 /** |
| |
387 * An event used to initialize the clone of a connection. |
| |
388 * |
| |
389 * Must be executed on the clone's async execution thread. |
| |
390 */ |
| |
391 class AsyncInitializeClone MOZ_FINAL: public nsRunnable |
| |
392 { |
| |
393 public: |
| |
394 /** |
| |
395 * @param aConnection The connection being cloned. |
| |
396 * @param aClone The clone. |
| |
397 * @param aReadOnly If |true|, the clone is read only. |
| |
398 * @param aCallback A callback to trigger once initialization |
| |
399 * is complete. This event will be called on |
| |
400 * aClone->threadOpenedOn. |
| |
401 */ |
| |
402 AsyncInitializeClone(Connection* aConnection, |
| |
403 Connection* aClone, |
| |
404 const bool aReadOnly, |
| |
405 mozIStorageCompletionCallback* aCallback) |
| |
406 : mConnection(aConnection) |
| |
407 , mClone(aClone) |
| |
408 , mReadOnly(aReadOnly) |
| |
409 , mCallback(aCallback) |
| |
410 { |
| |
411 MOZ_ASSERT(NS_IsMainThread()); |
| |
412 } |
| |
413 |
| |
414 NS_IMETHOD Run() { |
| |
415 MOZ_ASSERT (NS_GetCurrentThread() == mClone->getAsyncExecutionTarget()); |
| |
416 |
| |
417 nsresult rv = mConnection->initializeClone(mClone, mReadOnly); |
| |
418 if (NS_FAILED(rv)) { |
| |
419 return Dispatch(rv, nullptr); |
| |
420 } |
| |
421 return Dispatch(NS_OK, |
| |
422 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone)); |
| |
423 } |
| |
424 |
| |
425 private: |
| |
426 nsresult Dispatch(nsresult aResult, nsISupports* aValue) { |
| |
427 nsRefPtr<CallbackComplete> event = new CallbackComplete(aResult, |
| |
428 aValue, |
| |
429 mCallback.forget()); |
| |
430 return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL); |
| |
431 } |
| |
432 |
| |
433 ~AsyncInitializeClone() { |
| |
434 nsCOMPtr<nsIThread> thread; |
| |
435 DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread)); |
| |
436 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
| |
437 |
| |
438 // Handle ambiguous nsISupports inheritance. |
| |
439 Connection *rawConnection = nullptr; |
| |
440 mConnection.swap(rawConnection); |
| |
441 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, |
| |
442 rawConnection)); |
| |
443 |
| |
444 Connection *rawClone = nullptr; |
| |
445 mClone.swap(rawClone); |
| |
446 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, |
| |
447 rawClone)); |
| |
448 |
| |
449 // Generally, the callback will be released by CallbackComplete. |
| |
450 // However, if for some reason Run() is not executed, we still |
| |
451 // need to ensure that it is released here. |
| |
452 mozIStorageCompletionCallback *rawCallback = nullptr; |
| |
453 mCallback.swap(rawCallback); |
| |
454 (void)NS_ProxyRelease(thread, rawCallback); |
| |
455 } |
| |
456 |
| |
457 nsRefPtr<Connection> mConnection; |
| |
458 nsRefPtr<Connection> mClone; |
| |
459 const bool mReadOnly; |
| |
460 nsCOMPtr<mozIStorageCompletionCallback> mCallback; |
| |
461 }; |
| |
462 |
| |
463 } // anonymous namespace |
| |
464 |
| |
465 //////////////////////////////////////////////////////////////////////////////// |
| |
466 //// Connection |
| |
467 |
| |
468 Connection::Connection(Service *aService, |
| |
469 int aFlags, |
| |
470 bool aAsyncOnly) |
| |
471 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex") |
| |
472 , sharedDBMutex("Connection::sharedDBMutex") |
| |
473 , threadOpenedOn(do_GetCurrentThread()) |
| |
474 , mDBConn(nullptr) |
| |
475 , mAsyncExecutionThreadShuttingDown(false) |
| |
476 , mConnectionClosed(false) |
| |
477 , mTransactionInProgress(false) |
| |
478 , mProgressHandler(nullptr) |
| |
479 , mFlags(aFlags) |
| |
480 , mStorageService(aService) |
| |
481 , mAsyncOnly(aAsyncOnly) |
| |
482 { |
| |
483 mStorageService->registerConnection(this); |
| |
484 } |
| |
485 |
| |
486 Connection::~Connection() |
| |
487 { |
| |
488 (void)Close(); |
| |
489 |
| |
490 MOZ_ASSERT(!mAsyncExecutionThread, |
| |
491 "AsyncClose has not been invoked on this connection!"); |
| |
492 } |
| |
493 |
| |
494 NS_IMPL_ADDREF(Connection) |
| |
495 |
| |
496 NS_INTERFACE_MAP_BEGIN(Connection) |
| |
497 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection) |
| |
498 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) |
| |
499 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly) |
| |
500 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection) |
| |
501 NS_INTERFACE_MAP_END |
| |
502 |
| |
503 // This is identical to what NS_IMPL_RELEASE provides, but with the |
| |
504 // extra |1 == count| case. |
| |
505 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void) |
| |
506 { |
| |
507 NS_PRECONDITION(0 != mRefCnt, "dup release"); |
| |
508 nsrefcnt count = --mRefCnt; |
| |
509 NS_LOG_RELEASE(this, count, "Connection"); |
| |
510 if (1 == count) { |
| |
511 // If the refcount is 1, the single reference must be from |
| |
512 // gService->mConnections (in class |Service|). Which means we can |
| |
513 // unregister it safely. |
| |
514 mStorageService->unregisterConnection(this); |
| |
515 } else if (0 == count) { |
| |
516 mRefCnt = 1; /* stabilize */ |
| |
517 #if 0 /* enable this to find non-threadsafe destructors: */ |
| |
518 NS_ASSERT_OWNINGTHREAD(Connection); |
| |
519 #endif |
| |
520 delete (this); |
| |
521 return 0; |
| |
522 } |
| |
523 return count; |
| |
524 } |
| |
525 |
| |
526 int32_t |
| |
527 Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue) |
| |
528 { |
| |
529 MOZ_ASSERT(mDBConn, "A connection must exist at this point"); |
| |
530 int curr = 0, max = 0; |
| |
531 DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0); |
| |
532 MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc))); |
| |
533 if (aMaxValue) |
| |
534 *aMaxValue = max; |
| |
535 return curr; |
| |
536 } |
| |
537 |
| |
538 nsIEventTarget * |
| |
539 Connection::getAsyncExecutionTarget() |
| |
540 { |
| |
541 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
542 |
| |
543 // If we are shutting down the asynchronous thread, don't hand out any more |
| |
544 // references to the thread. |
| |
545 if (mAsyncExecutionThreadShuttingDown) |
| |
546 return nullptr; |
| |
547 |
| |
548 if (!mAsyncExecutionThread) { |
| |
549 nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread)); |
| |
550 if (NS_FAILED(rv)) { |
| |
551 NS_WARNING("Failed to create async thread."); |
| |
552 return nullptr; |
| |
553 } |
| |
554 static nsThreadPoolNaming naming; |
| |
555 naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"), |
| |
556 mAsyncExecutionThread); |
| |
557 } |
| |
558 |
| |
559 return mAsyncExecutionThread; |
| |
560 } |
| |
561 |
| |
562 nsresult |
| |
563 Connection::initialize() |
| |
564 { |
| |
565 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); |
| |
566 PROFILER_LABEL("storage", "Connection::initialize"); |
| |
567 |
| |
568 // in memory database requested, sqlite uses a magic file name |
| |
569 int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr); |
| |
570 if (srv != SQLITE_OK) { |
| |
571 mDBConn = nullptr; |
| |
572 return convertResultCode(srv); |
| |
573 } |
| |
574 |
| |
575 return initializeInternal(nullptr); |
| |
576 } |
| |
577 |
| |
578 nsresult |
| |
579 Connection::initialize(nsIFile *aDatabaseFile) |
| |
580 { |
| |
581 NS_ASSERTION (aDatabaseFile, "Passed null file!"); |
| |
582 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); |
| |
583 PROFILER_LABEL("storage", "Connection::initialize"); |
| |
584 |
| |
585 mDatabaseFile = aDatabaseFile; |
| |
586 |
| |
587 nsAutoString path; |
| |
588 nsresult rv = aDatabaseFile->GetPath(path); |
| |
589 NS_ENSURE_SUCCESS(rv, rv); |
| |
590 |
| |
591 int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, |
| |
592 mFlags, nullptr); |
| |
593 if (srv != SQLITE_OK) { |
| |
594 mDBConn = nullptr; |
| |
595 return convertResultCode(srv); |
| |
596 } |
| |
597 |
| |
598 rv = initializeInternal(aDatabaseFile); |
| |
599 NS_ENSURE_SUCCESS(rv, rv); |
| |
600 |
| |
601 mDatabaseFile = aDatabaseFile; |
| |
602 |
| |
603 return NS_OK; |
| |
604 } |
| |
605 |
| |
606 nsresult |
| |
607 Connection::initialize(nsIFileURL *aFileURL) |
| |
608 { |
| |
609 NS_ASSERTION (aFileURL, "Passed null file URL!"); |
| |
610 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); |
| |
611 PROFILER_LABEL("storage", "Connection::initialize"); |
| |
612 |
| |
613 nsCOMPtr<nsIFile> databaseFile; |
| |
614 nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile)); |
| |
615 NS_ENSURE_SUCCESS(rv, rv); |
| |
616 |
| |
617 nsAutoCString spec; |
| |
618 rv = aFileURL->GetSpec(spec); |
| |
619 NS_ENSURE_SUCCESS(rv, rv); |
| |
620 |
| |
621 int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr); |
| |
622 if (srv != SQLITE_OK) { |
| |
623 mDBConn = nullptr; |
| |
624 return convertResultCode(srv); |
| |
625 } |
| |
626 |
| |
627 rv = initializeInternal(databaseFile); |
| |
628 NS_ENSURE_SUCCESS(rv, rv); |
| |
629 |
| |
630 mFileURL = aFileURL; |
| |
631 mDatabaseFile = databaseFile; |
| |
632 |
| |
633 return NS_OK; |
| |
634 } |
| |
635 |
| |
636 |
| |
637 nsresult |
| |
638 Connection::initializeInternal(nsIFile* aDatabaseFile) |
| |
639 { |
| |
640 // Properly wrap the database handle's mutex. |
| |
641 sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn)); |
| |
642 |
| |
643 #ifdef PR_LOGGING |
| |
644 if (!gStorageLog) |
| |
645 gStorageLog = ::PR_NewLogModule("mozStorage"); |
| |
646 |
| |
647 ::sqlite3_trace(mDBConn, tracefunc, this); |
| |
648 |
| |
649 nsAutoCString leafName(":memory"); |
| |
650 if (aDatabaseFile) |
| |
651 (void)aDatabaseFile->GetNativeLeafName(leafName); |
| |
652 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)", |
| |
653 leafName.get(), this)); |
| |
654 #endif |
| |
655 |
| |
656 int64_t pageSize = Service::getDefaultPageSize(); |
| |
657 |
| |
658 // Set page_size to the preferred default value. This is effective only if |
| |
659 // the database has just been created, otherwise, if the database does not |
| |
660 // use WAL journal mode, a VACUUM operation will updated its page_size. |
| |
661 nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR |
| |
662 "PRAGMA page_size = "); |
| |
663 pageSizeQuery.AppendInt(pageSize); |
| |
664 nsresult rv = ExecuteSimpleSQL(pageSizeQuery); |
| |
665 NS_ENSURE_SUCCESS(rv, rv); |
| |
666 |
| |
667 // Setting the cache_size forces the database open, verifying if it is valid |
| |
668 // or corrupt. So this is executed regardless it being actually needed. |
| |
669 // The cache_size is calculated from the actual page_size, to save memory. |
| |
670 nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR |
| |
671 "PRAGMA cache_size = "); |
| |
672 cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES); |
| |
673 int srv = executeSql(mDBConn, cacheSizeQuery.get()); |
| |
674 if (srv != SQLITE_OK) { |
| |
675 ::sqlite3_close(mDBConn); |
| |
676 mDBConn = nullptr; |
| |
677 return convertResultCode(srv); |
| |
678 } |
| |
679 |
| |
680 // Register our built-in SQL functions. |
| |
681 srv = registerFunctions(mDBConn); |
| |
682 if (srv != SQLITE_OK) { |
| |
683 ::sqlite3_close(mDBConn); |
| |
684 mDBConn = nullptr; |
| |
685 return convertResultCode(srv); |
| |
686 } |
| |
687 |
| |
688 // Register our built-in SQL collating sequences. |
| |
689 srv = registerCollations(mDBConn, mStorageService); |
| |
690 if (srv != SQLITE_OK) { |
| |
691 ::sqlite3_close(mDBConn); |
| |
692 mDBConn = nullptr; |
| |
693 return convertResultCode(srv); |
| |
694 } |
| |
695 |
| |
696 // Set the synchronous PRAGMA, according to the preference. |
| |
697 switch (Service::getSynchronousPref()) { |
| |
698 case 2: |
| |
699 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
| |
700 "PRAGMA synchronous = FULL;")); |
| |
701 break; |
| |
702 case 0: |
| |
703 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
| |
704 "PRAGMA synchronous = OFF;")); |
| |
705 break; |
| |
706 case 1: |
| |
707 default: |
| |
708 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
| |
709 "PRAGMA synchronous = NORMAL;")); |
| |
710 break; |
| |
711 } |
| |
712 |
| |
713 return NS_OK; |
| |
714 } |
| |
715 |
| |
716 nsresult |
| |
717 Connection::databaseElementExists(enum DatabaseElementType aElementType, |
| |
718 const nsACString &aElementName, |
| |
719 bool *_exists) |
| |
720 { |
| |
721 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
722 |
| |
723 // When constructing the query, make sure to SELECT the correct db's sqlite_master |
| |
724 // if the user is prefixing the element with a specific db. ex: sample.test |
| |
725 nsCString query("SELECT name FROM (SELECT * FROM "); |
| |
726 nsDependentCSubstring element; |
| |
727 int32_t ind = aElementName.FindChar('.'); |
| |
728 if (ind == kNotFound) { |
| |
729 element.Assign(aElementName); |
| |
730 } |
| |
731 else { |
| |
732 nsDependentCSubstring db(Substring(aElementName, 0, ind + 1)); |
| |
733 element.Assign(Substring(aElementName, ind + 1, aElementName.Length())); |
| |
734 query.Append(db); |
| |
735 } |
| |
736 query.Append("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '"); |
| |
737 |
| |
738 switch (aElementType) { |
| |
739 case INDEX: |
| |
740 query.Append("index"); |
| |
741 break; |
| |
742 case TABLE: |
| |
743 query.Append("table"); |
| |
744 break; |
| |
745 } |
| |
746 query.Append("' AND name ='"); |
| |
747 query.Append(element); |
| |
748 query.Append("'"); |
| |
749 |
| |
750 sqlite3_stmt *stmt; |
| |
751 int srv = prepareStatement(mDBConn, query, &stmt); |
| |
752 if (srv != SQLITE_OK) |
| |
753 return convertResultCode(srv); |
| |
754 |
| |
755 srv = stepStatement(mDBConn, stmt); |
| |
756 // we just care about the return value from step |
| |
757 (void)::sqlite3_finalize(stmt); |
| |
758 |
| |
759 if (srv == SQLITE_ROW) { |
| |
760 *_exists = true; |
| |
761 return NS_OK; |
| |
762 } |
| |
763 if (srv == SQLITE_DONE) { |
| |
764 *_exists = false; |
| |
765 return NS_OK; |
| |
766 } |
| |
767 |
| |
768 return convertResultCode(srv); |
| |
769 } |
| |
770 |
| |
771 bool |
| |
772 Connection::findFunctionByInstance(nsISupports *aInstance) |
| |
773 { |
| |
774 sharedDBMutex.assertCurrentThreadOwns(); |
| |
775 FFEArguments args = { aInstance, false }; |
| |
776 (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args); |
| |
777 return args.found; |
| |
778 } |
| |
779 |
| |
780 /* static */ int |
| |
781 Connection::sProgressHelper(void *aArg) |
| |
782 { |
| |
783 Connection *_this = static_cast<Connection *>(aArg); |
| |
784 return _this->progressHandler(); |
| |
785 } |
| |
786 |
| |
787 int |
| |
788 Connection::progressHandler() |
| |
789 { |
| |
790 sharedDBMutex.assertCurrentThreadOwns(); |
| |
791 if (mProgressHandler) { |
| |
792 bool result; |
| |
793 nsresult rv = mProgressHandler->OnProgress(this, &result); |
| |
794 if (NS_FAILED(rv)) return 0; // Don't break request |
| |
795 return result ? 1 : 0; |
| |
796 } |
| |
797 return 0; |
| |
798 } |
| |
799 |
| |
800 nsresult |
| |
801 Connection::setClosedState() |
| |
802 { |
| |
803 // Ensure that we are on the correct thread to close the database. |
| |
804 bool onOpenedThread; |
| |
805 nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread); |
| |
806 NS_ENSURE_SUCCESS(rv, rv); |
| |
807 if (!onOpenedThread) { |
| |
808 NS_ERROR("Must close the database on the thread that you opened it with!"); |
| |
809 return NS_ERROR_UNEXPECTED; |
| |
810 } |
| |
811 |
| |
812 // Flag that we are shutting down the async thread, so that |
| |
813 // getAsyncExecutionTarget knows not to expose/create the async thread. |
| |
814 { |
| |
815 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
816 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED); |
| |
817 mAsyncExecutionThreadShuttingDown = true; |
| |
818 } |
| |
819 |
| |
820 // Set the property to null before closing the connection, otherwise the other |
| |
821 // functions in the module may try to use the connection after it is closed. |
| |
822 mDBConn = nullptr; |
| |
823 |
| |
824 return NS_OK; |
| |
825 } |
| |
826 |
| |
827 bool |
| |
828 Connection::connectionReady() |
| |
829 { |
| |
830 return mDBConn != nullptr; |
| |
831 } |
| |
832 |
| |
833 bool |
| |
834 Connection::isClosing() |
| |
835 { |
| |
836 bool shuttingDown = false; |
| |
837 { |
| |
838 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
839 shuttingDown = mAsyncExecutionThreadShuttingDown; |
| |
840 } |
| |
841 return shuttingDown && !isClosed(); |
| |
842 } |
| |
843 |
| |
844 bool |
| |
845 Connection::isClosed() |
| |
846 { |
| |
847 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
848 return mConnectionClosed; |
| |
849 } |
| |
850 |
| |
851 nsresult |
| |
852 Connection::internalClose(sqlite3 *aNativeConnection) |
| |
853 { |
| |
854 // Sanity checks to make sure we are in the proper state before calling this. |
| |
855 MOZ_ASSERT(aNativeConnection, "Database connection is invalid!"); |
| |
856 MOZ_ASSERT(!isClosed()); |
| |
857 |
| |
858 #ifdef DEBUG |
| |
859 { // Make sure we have marked our async thread as shutting down. |
| |
860 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
861 NS_ASSERTION(mAsyncExecutionThreadShuttingDown, |
| |
862 "Did not call setClosedState!"); |
| |
863 } |
| |
864 #endif // DEBUG |
| |
865 |
| |
866 #ifdef PR_LOGGING |
| |
867 nsAutoCString leafName(":memory"); |
| |
868 if (mDatabaseFile) |
| |
869 (void)mDatabaseFile->GetNativeLeafName(leafName); |
| |
870 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'", |
| |
871 leafName.get())); |
| |
872 #endif |
| |
873 |
| |
874 // At this stage, we may still have statements that need to be |
| |
875 // finalized. Attempt to close the database connection. This will |
| |
876 // always disconnect any virtual tables and cleanly finalize their |
| |
877 // internal statements. Once this is done, closing may fail due to |
| |
878 // unfinalized client statements, in which case we need to finalize |
| |
879 // these statements and close again. |
| |
880 { |
| |
881 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
882 mConnectionClosed = true; |
| |
883 } |
| |
884 int srv = sqlite3_close(aNativeConnection); |
| |
885 |
| |
886 if (srv == SQLITE_BUSY) { |
| |
887 // We still have non-finalized statements. Finalize them. |
| |
888 |
| |
889 sqlite3_stmt *stmt = nullptr; |
| |
890 while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) { |
| |
891 PR_LOG(gStorageLog, PR_LOG_NOTICE, |
| |
892 ("Auto-finalizing SQL statement '%s' (%x)", |
| |
893 ::sqlite3_sql(stmt), |
| |
894 stmt)); |
| |
895 |
| |
896 #ifdef DEBUG |
| |
897 char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection", |
| |
898 ::sqlite3_sql(stmt), |
| |
899 stmt); |
| |
900 NS_WARNING(msg); |
| |
901 ::PR_smprintf_free(msg); |
| |
902 #endif // DEBUG |
| |
903 |
| |
904 srv = ::sqlite3_finalize(stmt); |
| |
905 |
| |
906 #ifdef DEBUG |
| |
907 if (srv != SQLITE_OK) { |
| |
908 char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)", |
| |
909 ::sqlite3_sql(stmt), |
| |
910 stmt); |
| |
911 NS_WARNING(msg); |
| |
912 ::PR_smprintf_free(msg); |
| |
913 } |
| |
914 #endif // DEBUG |
| |
915 |
| |
916 // Ensure that the loop continues properly, whether closing has succeeded |
| |
917 // or not. |
| |
918 if (srv == SQLITE_OK) { |
| |
919 stmt = nullptr; |
| |
920 } |
| |
921 } |
| |
922 |
| |
923 // Now that all statements have been finalized, we |
| |
924 // should be able to close. |
| |
925 srv = ::sqlite3_close(aNativeConnection); |
| |
926 |
| |
927 } |
| |
928 |
| |
929 if (srv != SQLITE_OK) { |
| |
930 MOZ_ASSERT(srv == SQLITE_OK, |
| |
931 "sqlite3_close failed. There are probably outstanding statements that are listed above!"); |
| |
932 } |
| |
933 |
| |
934 return convertResultCode(srv); |
| |
935 } |
| |
936 |
| |
937 nsCString |
| |
938 Connection::getFilename() |
| |
939 { |
| |
940 nsCString leafname(":memory:"); |
| |
941 if (mDatabaseFile) { |
| |
942 (void)mDatabaseFile->GetNativeLeafName(leafname); |
| |
943 } |
| |
944 return leafname; |
| |
945 } |
| |
946 |
| |
947 int |
| |
948 Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement) |
| |
949 { |
| |
950 MOZ_ASSERT(aStatement); |
| |
951 bool checkedMainThread = false; |
| |
952 TimeStamp startTime = TimeStamp::Now(); |
| |
953 |
| |
954 // The connection may have been closed if the executing statement has been |
| |
955 // created and cached after a call to asyncClose() but before the actual |
| |
956 // sqlite3_close(). This usually happens when other tasks using cached |
| |
957 // statements are asynchronously scheduled for execution and any of them ends |
| |
958 // up after asyncClose. See bug 728653 for details. |
| |
959 if (isClosed()) |
| |
960 return SQLITE_MISUSE; |
| |
961 |
| |
962 (void)::sqlite3_extended_result_codes(aNativeConnection, 1); |
| |
963 |
| |
964 int srv; |
| |
965 while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) { |
| |
966 if (!checkedMainThread) { |
| |
967 checkedMainThread = true; |
| |
968 if (::NS_IsMainThread()) { |
| |
969 NS_WARNING("We won't allow blocking on the main thread!"); |
| |
970 break; |
| |
971 } |
| |
972 } |
| |
973 |
| |
974 srv = WaitForUnlockNotify(aNativeConnection); |
| |
975 if (srv != SQLITE_OK) { |
| |
976 break; |
| |
977 } |
| |
978 |
| |
979 ::sqlite3_reset(aStatement); |
| |
980 } |
| |
981 |
| |
982 // Report very slow SQL statements to Telemetry |
| |
983 TimeDuration duration = TimeStamp::Now() - startTime; |
| |
984 const uint32_t threshold = |
| |
985 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread |
| |
986 : Telemetry::kSlowSQLThresholdForHelperThreads; |
| |
987 if (duration.ToMilliseconds() >= threshold) { |
| |
988 nsDependentCString statementString(::sqlite3_sql(aStatement)); |
| |
989 Telemetry::RecordSlowSQLStatement(statementString, getFilename(), |
| |
990 duration.ToMilliseconds()); |
| |
991 } |
| |
992 |
| |
993 (void)::sqlite3_extended_result_codes(aNativeConnection, 0); |
| |
994 // Drop off the extended result bits of the result code. |
| |
995 return srv & 0xFF; |
| |
996 } |
| |
997 |
| |
998 int |
| |
999 Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL, |
| |
1000 sqlite3_stmt **_stmt) |
| |
1001 { |
| |
1002 // We should not even try to prepare statements after the connection has |
| |
1003 // been closed. |
| |
1004 if (isClosed()) |
| |
1005 return SQLITE_MISUSE; |
| |
1006 |
| |
1007 bool checkedMainThread = false; |
| |
1008 |
| |
1009 (void)::sqlite3_extended_result_codes(aNativeConnection, 1); |
| |
1010 |
| |
1011 int srv; |
| |
1012 while((srv = ::sqlite3_prepare_v2(aNativeConnection, |
| |
1013 aSQL.get(), |
| |
1014 -1, |
| |
1015 _stmt, |
| |
1016 nullptr)) == SQLITE_LOCKED_SHAREDCACHE) { |
| |
1017 if (!checkedMainThread) { |
| |
1018 checkedMainThread = true; |
| |
1019 if (::NS_IsMainThread()) { |
| |
1020 NS_WARNING("We won't allow blocking on the main thread!"); |
| |
1021 break; |
| |
1022 } |
| |
1023 } |
| |
1024 |
| |
1025 srv = WaitForUnlockNotify(aNativeConnection); |
| |
1026 if (srv != SQLITE_OK) { |
| |
1027 break; |
| |
1028 } |
| |
1029 } |
| |
1030 |
| |
1031 if (srv != SQLITE_OK) { |
| |
1032 nsCString warnMsg; |
| |
1033 warnMsg.AppendLiteral("The SQL statement '"); |
| |
1034 warnMsg.Append(aSQL); |
| |
1035 warnMsg.AppendLiteral("' could not be compiled due to an error: "); |
| |
1036 warnMsg.Append(::sqlite3_errmsg(aNativeConnection)); |
| |
1037 |
| |
1038 #ifdef DEBUG |
| |
1039 NS_WARNING(warnMsg.get()); |
| |
1040 #endif |
| |
1041 PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get())); |
| |
1042 } |
| |
1043 |
| |
1044 (void)::sqlite3_extended_result_codes(aNativeConnection, 0); |
| |
1045 // Drop off the extended result bits of the result code. |
| |
1046 int rc = srv & 0xFF; |
| |
1047 // sqlite will return OK on a comment only string and set _stmt to nullptr. |
| |
1048 // The callers of this function are used to only checking the return value, |
| |
1049 // so it is safer to return an error code. |
| |
1050 if (rc == SQLITE_OK && *_stmt == nullptr) { |
| |
1051 return SQLITE_MISUSE; |
| |
1052 } |
| |
1053 |
| |
1054 return rc; |
| |
1055 } |
| |
1056 |
| |
1057 |
| |
1058 int |
| |
1059 Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString) |
| |
1060 { |
| |
1061 if (isClosed()) |
| |
1062 return SQLITE_MISUSE; |
| |
1063 |
| |
1064 TimeStamp startTime = TimeStamp::Now(); |
| |
1065 int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr, |
| |
1066 nullptr); |
| |
1067 |
| |
1068 // Report very slow SQL statements to Telemetry |
| |
1069 TimeDuration duration = TimeStamp::Now() - startTime; |
| |
1070 const uint32_t threshold = |
| |
1071 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread |
| |
1072 : Telemetry::kSlowSQLThresholdForHelperThreads; |
| |
1073 if (duration.ToMilliseconds() >= threshold) { |
| |
1074 nsDependentCString statementString(aSqlString); |
| |
1075 Telemetry::RecordSlowSQLStatement(statementString, getFilename(), |
| |
1076 duration.ToMilliseconds()); |
| |
1077 } |
| |
1078 |
| |
1079 return srv; |
| |
1080 } |
| |
1081 |
| |
1082 //////////////////////////////////////////////////////////////////////////////// |
| |
1083 //// nsIInterfaceRequestor |
| |
1084 |
| |
1085 NS_IMETHODIMP |
| |
1086 Connection::GetInterface(const nsIID &aIID, |
| |
1087 void **_result) |
| |
1088 { |
| |
1089 if (aIID.Equals(NS_GET_IID(nsIEventTarget))) { |
| |
1090 nsIEventTarget *background = getAsyncExecutionTarget(); |
| |
1091 NS_IF_ADDREF(background); |
| |
1092 *_result = background; |
| |
1093 return NS_OK; |
| |
1094 } |
| |
1095 return NS_ERROR_NO_INTERFACE; |
| |
1096 } |
| |
1097 |
| |
1098 //////////////////////////////////////////////////////////////////////////////// |
| |
1099 //// mozIStorageConnection |
| |
1100 |
| |
1101 NS_IMETHODIMP |
| |
1102 Connection::Close() |
| |
1103 { |
| |
1104 if (!mDBConn) |
| |
1105 return NS_ERROR_NOT_INITIALIZED; |
| |
1106 |
| |
1107 { // Make sure we have not executed any asynchronous statements. |
| |
1108 // If this fails, the mDBConn will be left open, resulting in a leak. |
| |
1109 // Ideally we'd schedule some code to destroy the mDBConn once all its |
| |
1110 // async statements have finished executing; see bug 704030. |
| |
1111 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
1112 bool asyncCloseWasCalled = !mAsyncExecutionThread; |
| |
1113 NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED); |
| |
1114 } |
| |
1115 |
| |
1116 // setClosedState nullifies our connection pointer, so we take a raw pointer |
| |
1117 // off it, to pass it through the close procedure. |
| |
1118 sqlite3 *nativeConn = mDBConn; |
| |
1119 nsresult rv = setClosedState(); |
| |
1120 NS_ENSURE_SUCCESS(rv, rv); |
| |
1121 |
| |
1122 return internalClose(nativeConn); |
| |
1123 } |
| |
1124 |
| |
1125 NS_IMETHODIMP |
| |
1126 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback) |
| |
1127 { |
| |
1128 if (!NS_IsMainThread()) { |
| |
1129 return NS_ERROR_NOT_SAME_THREAD; |
| |
1130 } |
| |
1131 if (!mDBConn) |
| |
1132 return NS_ERROR_NOT_INITIALIZED; |
| |
1133 |
| |
1134 nsIEventTarget *asyncThread = getAsyncExecutionTarget(); |
| |
1135 NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED); |
| |
1136 |
| |
1137 // setClosedState nullifies our connection pointer, so we take a raw pointer |
| |
1138 // off it, to pass it through the close procedure. |
| |
1139 sqlite3 *nativeConn = mDBConn; |
| |
1140 nsresult rv = setClosedState(); |
| |
1141 NS_ENSURE_SUCCESS(rv, rv); |
| |
1142 |
| |
1143 // Create our callback event if we were given a callback. |
| |
1144 nsCOMPtr<nsIRunnable> completeEvent; |
| |
1145 if (aCallback) { |
| |
1146 completeEvent = newCompletionEvent(aCallback); |
| |
1147 } |
| |
1148 |
| |
1149 // Create and dispatch our close event to the background thread. |
| |
1150 nsCOMPtr<nsIRunnable> closeEvent; |
| |
1151 { |
| |
1152 // We need to lock because we're modifying mAsyncExecutionThread |
| |
1153 MutexAutoLock lockedScope(sharedAsyncExecutionMutex); |
| |
1154 closeEvent = new AsyncCloseConnection(this, |
| |
1155 nativeConn, |
| |
1156 completeEvent, |
| |
1157 mAsyncExecutionThread.forget()); |
| |
1158 } |
| |
1159 |
| |
1160 rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL); |
| |
1161 NS_ENSURE_SUCCESS(rv, rv); |
| |
1162 |
| |
1163 return NS_OK; |
| |
1164 } |
| |
1165 |
| |
1166 NS_IMETHODIMP |
| |
1167 Connection::AsyncClone(bool aReadOnly, |
| |
1168 mozIStorageCompletionCallback *aCallback) |
| |
1169 { |
| |
1170 PROFILER_LABEL("storage", "Connection::Clone"); |
| |
1171 if (!NS_IsMainThread()) { |
| |
1172 return NS_ERROR_NOT_SAME_THREAD; |
| |
1173 } |
| |
1174 if (!mDBConn) |
| |
1175 return NS_ERROR_NOT_INITIALIZED; |
| |
1176 if (!mDatabaseFile) |
| |
1177 return NS_ERROR_UNEXPECTED; |
| |
1178 |
| |
1179 int flags = mFlags; |
| |
1180 if (aReadOnly) { |
| |
1181 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY. |
| |
1182 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY; |
| |
1183 // Turn off SQLITE_OPEN_CREATE. |
| |
1184 flags = (~SQLITE_OPEN_CREATE & flags); |
| |
1185 } |
| |
1186 |
| |
1187 nsRefPtr<Connection> clone = new Connection(mStorageService, flags, |
| |
1188 mAsyncOnly); |
| |
1189 |
| |
1190 nsRefPtr<AsyncInitializeClone> initEvent = |
| |
1191 new AsyncInitializeClone(this, clone, aReadOnly, aCallback); |
| |
1192 nsCOMPtr<nsIEventTarget> target = clone->getAsyncExecutionTarget(); |
| |
1193 if (!target) { |
| |
1194 return NS_ERROR_UNEXPECTED; |
| |
1195 } |
| |
1196 return target->Dispatch(initEvent, NS_DISPATCH_NORMAL); |
| |
1197 } |
| |
1198 |
| |
1199 nsresult |
| |
1200 Connection::initializeClone(Connection* aClone, bool aReadOnly) |
| |
1201 { |
| |
1202 nsresult rv = mFileURL ? aClone->initialize(mFileURL) |
| |
1203 : aClone->initialize(mDatabaseFile); |
| |
1204 if (NS_FAILED(rv)) { |
| |
1205 return rv; |
| |
1206 } |
| |
1207 |
| |
1208 // Copy over pragmas from the original connection. |
| |
1209 static const char * pragmas[] = { |
| |
1210 "cache_size", |
| |
1211 "temp_store", |
| |
1212 "foreign_keys", |
| |
1213 "journal_size_limit", |
| |
1214 "synchronous", |
| |
1215 "wal_autocheckpoint", |
| |
1216 }; |
| |
1217 for (uint32_t i = 0; i < ArrayLength(pragmas); ++i) { |
| |
1218 // Read-only connections just need cache_size and temp_store pragmas. |
| |
1219 if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 && |
| |
1220 ::strcmp(pragmas[i], "temp_store") != 0) { |
| |
1221 continue; |
| |
1222 } |
| |
1223 |
| |
1224 nsAutoCString pragmaQuery("PRAGMA "); |
| |
1225 pragmaQuery.Append(pragmas[i]); |
| |
1226 nsCOMPtr<mozIStorageStatement> stmt; |
| |
1227 rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt)); |
| |
1228 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
| |
1229 bool hasResult = false; |
| |
1230 if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
| |
1231 pragmaQuery.AppendLiteral(" = "); |
| |
1232 pragmaQuery.AppendInt(stmt->AsInt32(0)); |
| |
1233 rv = aClone->ExecuteSimpleSQL(pragmaQuery); |
| |
1234 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
| |
1235 } |
| |
1236 } |
| |
1237 |
| |
1238 // Copy any functions that have been added to this connection. |
| |
1239 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1240 (void)mFunctions.EnumerateRead(copyFunctionEnumerator, aClone); |
| |
1241 |
| |
1242 return NS_OK; |
| |
1243 } |
| |
1244 |
| |
1245 NS_IMETHODIMP |
| |
1246 Connection::Clone(bool aReadOnly, |
| |
1247 mozIStorageConnection **_connection) |
| |
1248 { |
| |
1249 MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread()); |
| |
1250 |
| |
1251 PROFILER_LABEL("storage", "Connection::Clone"); |
| |
1252 if (!mDBConn) |
| |
1253 return NS_ERROR_NOT_INITIALIZED; |
| |
1254 if (!mDatabaseFile) |
| |
1255 return NS_ERROR_UNEXPECTED; |
| |
1256 |
| |
1257 int flags = mFlags; |
| |
1258 if (aReadOnly) { |
| |
1259 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY. |
| |
1260 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY; |
| |
1261 // Turn off SQLITE_OPEN_CREATE. |
| |
1262 flags = (~SQLITE_OPEN_CREATE & flags); |
| |
1263 } |
| |
1264 |
| |
1265 nsRefPtr<Connection> clone = new Connection(mStorageService, flags, |
| |
1266 mAsyncOnly); |
| |
1267 |
| |
1268 nsresult rv = initializeClone(clone, aReadOnly); |
| |
1269 if (NS_FAILED(rv)) { |
| |
1270 return rv; |
| |
1271 } |
| |
1272 |
| |
1273 NS_IF_ADDREF(*_connection = clone); |
| |
1274 return NS_OK; |
| |
1275 } |
| |
1276 |
| |
1277 NS_IMETHODIMP |
| |
1278 Connection::GetDefaultPageSize(int32_t *_defaultPageSize) |
| |
1279 { |
| |
1280 *_defaultPageSize = Service::getDefaultPageSize(); |
| |
1281 return NS_OK; |
| |
1282 } |
| |
1283 |
| |
1284 NS_IMETHODIMP |
| |
1285 Connection::GetConnectionReady(bool *_ready) |
| |
1286 { |
| |
1287 *_ready = connectionReady(); |
| |
1288 return NS_OK; |
| |
1289 } |
| |
1290 |
| |
1291 NS_IMETHODIMP |
| |
1292 Connection::GetDatabaseFile(nsIFile **_dbFile) |
| |
1293 { |
| |
1294 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1295 |
| |
1296 NS_IF_ADDREF(*_dbFile = mDatabaseFile); |
| |
1297 |
| |
1298 return NS_OK; |
| |
1299 } |
| |
1300 |
| |
1301 NS_IMETHODIMP |
| |
1302 Connection::GetLastInsertRowID(int64_t *_id) |
| |
1303 { |
| |
1304 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1305 |
| |
1306 sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn); |
| |
1307 *_id = id; |
| |
1308 |
| |
1309 return NS_OK; |
| |
1310 } |
| |
1311 |
| |
1312 NS_IMETHODIMP |
| |
1313 Connection::GetAffectedRows(int32_t *_rows) |
| |
1314 { |
| |
1315 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1316 |
| |
1317 *_rows = ::sqlite3_changes(mDBConn); |
| |
1318 |
| |
1319 return NS_OK; |
| |
1320 } |
| |
1321 |
| |
1322 NS_IMETHODIMP |
| |
1323 Connection::GetLastError(int32_t *_error) |
| |
1324 { |
| |
1325 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1326 |
| |
1327 *_error = ::sqlite3_errcode(mDBConn); |
| |
1328 |
| |
1329 return NS_OK; |
| |
1330 } |
| |
1331 |
| |
1332 NS_IMETHODIMP |
| |
1333 Connection::GetLastErrorString(nsACString &_errorString) |
| |
1334 { |
| |
1335 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1336 |
| |
1337 const char *serr = ::sqlite3_errmsg(mDBConn); |
| |
1338 _errorString.Assign(serr); |
| |
1339 |
| |
1340 return NS_OK; |
| |
1341 } |
| |
1342 |
| |
1343 NS_IMETHODIMP |
| |
1344 Connection::GetSchemaVersion(int32_t *_version) |
| |
1345 { |
| |
1346 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1347 |
| |
1348 nsCOMPtr<mozIStorageStatement> stmt; |
| |
1349 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"), |
| |
1350 getter_AddRefs(stmt)); |
| |
1351 NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY); |
| |
1352 |
| |
1353 *_version = 0; |
| |
1354 bool hasResult; |
| |
1355 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) |
| |
1356 *_version = stmt->AsInt32(0); |
| |
1357 |
| |
1358 return NS_OK; |
| |
1359 } |
| |
1360 |
| |
1361 NS_IMETHODIMP |
| |
1362 Connection::SetSchemaVersion(int32_t aVersion) |
| |
1363 { |
| |
1364 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1365 |
| |
1366 nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = ")); |
| |
1367 stmt.AppendInt(aVersion); |
| |
1368 |
| |
1369 return ExecuteSimpleSQL(stmt); |
| |
1370 } |
| |
1371 |
| |
1372 NS_IMETHODIMP |
| |
1373 Connection::CreateStatement(const nsACString &aSQLStatement, |
| |
1374 mozIStorageStatement **_stmt) |
| |
1375 { |
| |
1376 NS_ENSURE_ARG_POINTER(_stmt); |
| |
1377 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1378 |
| |
1379 nsRefPtr<Statement> statement(new Statement()); |
| |
1380 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY); |
| |
1381 |
| |
1382 nsresult rv = statement->initialize(this, mDBConn, aSQLStatement); |
| |
1383 NS_ENSURE_SUCCESS(rv, rv); |
| |
1384 |
| |
1385 Statement *rawPtr; |
| |
1386 statement.forget(&rawPtr); |
| |
1387 *_stmt = rawPtr; |
| |
1388 return NS_OK; |
| |
1389 } |
| |
1390 |
| |
1391 NS_IMETHODIMP |
| |
1392 Connection::CreateAsyncStatement(const nsACString &aSQLStatement, |
| |
1393 mozIStorageAsyncStatement **_stmt) |
| |
1394 { |
| |
1395 NS_ENSURE_ARG_POINTER(_stmt); |
| |
1396 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1397 |
| |
1398 nsRefPtr<AsyncStatement> statement(new AsyncStatement()); |
| |
1399 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY); |
| |
1400 |
| |
1401 nsresult rv = statement->initialize(this, mDBConn, aSQLStatement); |
| |
1402 NS_ENSURE_SUCCESS(rv, rv); |
| |
1403 |
| |
1404 AsyncStatement *rawPtr; |
| |
1405 statement.forget(&rawPtr); |
| |
1406 *_stmt = rawPtr; |
| |
1407 return NS_OK; |
| |
1408 } |
| |
1409 |
| |
1410 NS_IMETHODIMP |
| |
1411 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement) |
| |
1412 { |
| |
1413 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1414 |
| |
1415 int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get()); |
| |
1416 return convertResultCode(srv); |
| |
1417 } |
| |
1418 |
| |
1419 NS_IMETHODIMP |
| |
1420 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements, |
| |
1421 uint32_t aNumStatements, |
| |
1422 mozIStorageStatementCallback *aCallback, |
| |
1423 mozIStoragePendingStatement **_handle) |
| |
1424 { |
| |
1425 nsTArray<StatementData> stmts(aNumStatements); |
| |
1426 for (uint32_t i = 0; i < aNumStatements; i++) { |
| |
1427 nsCOMPtr<StorageBaseStatementInternal> stmt = |
| |
1428 do_QueryInterface(aStatements[i]); |
| |
1429 |
| |
1430 // Obtain our StatementData. |
| |
1431 StatementData data; |
| |
1432 nsresult rv = stmt->getAsynchronousStatementData(data); |
| |
1433 NS_ENSURE_SUCCESS(rv, rv); |
| |
1434 |
| |
1435 NS_ASSERTION(stmt->getOwner() == this, |
| |
1436 "Statement must be from this database connection!"); |
| |
1437 |
| |
1438 // Now append it to our array. |
| |
1439 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY); |
| |
1440 } |
| |
1441 |
| |
1442 // Dispatch to the background |
| |
1443 return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback, |
| |
1444 _handle); |
| |
1445 } |
| |
1446 |
| |
1447 NS_IMETHODIMP |
| |
1448 Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement, |
| |
1449 mozIStorageStatementCallback *aCallback, |
| |
1450 mozIStoragePendingStatement **_handle) |
| |
1451 { |
| |
1452 if (!NS_IsMainThread()) { |
| |
1453 return NS_ERROR_NOT_SAME_THREAD; |
| |
1454 } |
| |
1455 |
| |
1456 nsCOMPtr<mozIStorageAsyncStatement> stmt; |
| |
1457 nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt)); |
| |
1458 if (NS_FAILED(rv)) { |
| |
1459 return rv; |
| |
1460 } |
| |
1461 |
| |
1462 nsCOMPtr<mozIStoragePendingStatement> pendingStatement; |
| |
1463 rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement)); |
| |
1464 if (NS_FAILED(rv)) { |
| |
1465 return rv; |
| |
1466 } |
| |
1467 |
| |
1468 NS_ADDREF(*_handle = pendingStatement); |
| |
1469 return rv; |
| |
1470 } |
| |
1471 |
| |
1472 NS_IMETHODIMP |
| |
1473 Connection::TableExists(const nsACString &aTableName, |
| |
1474 bool *_exists) |
| |
1475 { |
| |
1476 return databaseElementExists(TABLE, aTableName, _exists); |
| |
1477 } |
| |
1478 |
| |
1479 NS_IMETHODIMP |
| |
1480 Connection::IndexExists(const nsACString &aIndexName, |
| |
1481 bool* _exists) |
| |
1482 { |
| |
1483 return databaseElementExists(INDEX, aIndexName, _exists); |
| |
1484 } |
| |
1485 |
| |
1486 NS_IMETHODIMP |
| |
1487 Connection::GetTransactionInProgress(bool *_inProgress) |
| |
1488 { |
| |
1489 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1490 |
| |
1491 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1492 *_inProgress = mTransactionInProgress; |
| |
1493 return NS_OK; |
| |
1494 } |
| |
1495 |
| |
1496 NS_IMETHODIMP |
| |
1497 Connection::BeginTransaction() |
| |
1498 { |
| |
1499 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED); |
| |
1500 } |
| |
1501 |
| |
1502 NS_IMETHODIMP |
| |
1503 Connection::BeginTransactionAs(int32_t aTransactionType) |
| |
1504 { |
| |
1505 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1506 |
| |
1507 return beginTransactionInternal(mDBConn, aTransactionType); |
| |
1508 } |
| |
1509 |
| |
1510 nsresult |
| |
1511 Connection::beginTransactionInternal(sqlite3 *aNativeConnection, |
| |
1512 int32_t aTransactionType) |
| |
1513 { |
| |
1514 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1515 if (mTransactionInProgress) |
| |
1516 return NS_ERROR_FAILURE; |
| |
1517 nsresult rv; |
| |
1518 switch(aTransactionType) { |
| |
1519 case TRANSACTION_DEFERRED: |
| |
1520 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED")); |
| |
1521 break; |
| |
1522 case TRANSACTION_IMMEDIATE: |
| |
1523 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE")); |
| |
1524 break; |
| |
1525 case TRANSACTION_EXCLUSIVE: |
| |
1526 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE")); |
| |
1527 break; |
| |
1528 default: |
| |
1529 return NS_ERROR_ILLEGAL_VALUE; |
| |
1530 } |
| |
1531 if (NS_SUCCEEDED(rv)) |
| |
1532 mTransactionInProgress = true; |
| |
1533 return rv; |
| |
1534 } |
| |
1535 |
| |
1536 NS_IMETHODIMP |
| |
1537 Connection::CommitTransaction() |
| |
1538 { |
| |
1539 if (!mDBConn) |
| |
1540 return NS_ERROR_NOT_INITIALIZED; |
| |
1541 |
| |
1542 return commitTransactionInternal(mDBConn); |
| |
1543 } |
| |
1544 |
| |
1545 nsresult |
| |
1546 Connection::commitTransactionInternal(sqlite3 *aNativeConnection) |
| |
1547 { |
| |
1548 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1549 if (!mTransactionInProgress) |
| |
1550 return NS_ERROR_UNEXPECTED; |
| |
1551 nsresult rv = |
| |
1552 convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION")); |
| |
1553 if (NS_SUCCEEDED(rv)) |
| |
1554 mTransactionInProgress = false; |
| |
1555 return rv; |
| |
1556 } |
| |
1557 |
| |
1558 NS_IMETHODIMP |
| |
1559 Connection::RollbackTransaction() |
| |
1560 { |
| |
1561 if (!mDBConn) |
| |
1562 return NS_ERROR_NOT_INITIALIZED; |
| |
1563 |
| |
1564 return rollbackTransactionInternal(mDBConn); |
| |
1565 } |
| |
1566 |
| |
1567 nsresult |
| |
1568 Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection) |
| |
1569 { |
| |
1570 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1571 if (!mTransactionInProgress) |
| |
1572 return NS_ERROR_UNEXPECTED; |
| |
1573 |
| |
1574 nsresult rv = |
| |
1575 convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION")); |
| |
1576 if (NS_SUCCEEDED(rv)) |
| |
1577 mTransactionInProgress = false; |
| |
1578 return rv; |
| |
1579 } |
| |
1580 |
| |
1581 NS_IMETHODIMP |
| |
1582 Connection::CreateTable(const char *aTableName, |
| |
1583 const char *aTableSchema) |
| |
1584 { |
| |
1585 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1586 |
| |
1587 char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema); |
| |
1588 if (!buf) |
| |
1589 return NS_ERROR_OUT_OF_MEMORY; |
| |
1590 |
| |
1591 int srv = executeSql(mDBConn, buf); |
| |
1592 ::PR_smprintf_free(buf); |
| |
1593 |
| |
1594 return convertResultCode(srv); |
| |
1595 } |
| |
1596 |
| |
1597 NS_IMETHODIMP |
| |
1598 Connection::CreateFunction(const nsACString &aFunctionName, |
| |
1599 int32_t aNumArguments, |
| |
1600 mozIStorageFunction *aFunction) |
| |
1601 { |
| |
1602 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1603 |
| |
1604 // Check to see if this function is already defined. We only check the name |
| |
1605 // because a function can be defined with the same body but different names. |
| |
1606 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1607 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE); |
| |
1608 |
| |
1609 int srv = ::sqlite3_create_function(mDBConn, |
| |
1610 nsPromiseFlatCString(aFunctionName).get(), |
| |
1611 aNumArguments, |
| |
1612 SQLITE_ANY, |
| |
1613 aFunction, |
| |
1614 basicFunctionHelper, |
| |
1615 nullptr, |
| |
1616 nullptr); |
| |
1617 if (srv != SQLITE_OK) |
| |
1618 return convertResultCode(srv); |
| |
1619 |
| |
1620 FunctionInfo info = { aFunction, |
| |
1621 Connection::FunctionInfo::SIMPLE, |
| |
1622 aNumArguments }; |
| |
1623 mFunctions.Put(aFunctionName, info); |
| |
1624 |
| |
1625 return NS_OK; |
| |
1626 } |
| |
1627 |
| |
1628 NS_IMETHODIMP |
| |
1629 Connection::CreateAggregateFunction(const nsACString &aFunctionName, |
| |
1630 int32_t aNumArguments, |
| |
1631 mozIStorageAggregateFunction *aFunction) |
| |
1632 { |
| |
1633 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1634 |
| |
1635 // Check to see if this function name is already defined. |
| |
1636 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1637 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE); |
| |
1638 |
| |
1639 // Because aggregate functions depend on state across calls, you cannot have |
| |
1640 // the same instance use the same name. We want to enumerate all functions |
| |
1641 // and make sure this instance is not already registered. |
| |
1642 NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE); |
| |
1643 |
| |
1644 int srv = ::sqlite3_create_function(mDBConn, |
| |
1645 nsPromiseFlatCString(aFunctionName).get(), |
| |
1646 aNumArguments, |
| |
1647 SQLITE_ANY, |
| |
1648 aFunction, |
| |
1649 nullptr, |
| |
1650 aggregateFunctionStepHelper, |
| |
1651 aggregateFunctionFinalHelper); |
| |
1652 if (srv != SQLITE_OK) |
| |
1653 return convertResultCode(srv); |
| |
1654 |
| |
1655 FunctionInfo info = { aFunction, |
| |
1656 Connection::FunctionInfo::AGGREGATE, |
| |
1657 aNumArguments }; |
| |
1658 mFunctions.Put(aFunctionName, info); |
| |
1659 |
| |
1660 return NS_OK; |
| |
1661 } |
| |
1662 |
| |
1663 NS_IMETHODIMP |
| |
1664 Connection::RemoveFunction(const nsACString &aFunctionName) |
| |
1665 { |
| |
1666 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1667 |
| |
1668 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1669 NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE); |
| |
1670 |
| |
1671 int srv = ::sqlite3_create_function(mDBConn, |
| |
1672 nsPromiseFlatCString(aFunctionName).get(), |
| |
1673 0, |
| |
1674 SQLITE_ANY, |
| |
1675 nullptr, |
| |
1676 nullptr, |
| |
1677 nullptr, |
| |
1678 nullptr); |
| |
1679 if (srv != SQLITE_OK) |
| |
1680 return convertResultCode(srv); |
| |
1681 |
| |
1682 mFunctions.Remove(aFunctionName); |
| |
1683 |
| |
1684 return NS_OK; |
| |
1685 } |
| |
1686 |
| |
1687 NS_IMETHODIMP |
| |
1688 Connection::SetProgressHandler(int32_t aGranularity, |
| |
1689 mozIStorageProgressHandler *aHandler, |
| |
1690 mozIStorageProgressHandler **_oldHandler) |
| |
1691 { |
| |
1692 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1693 |
| |
1694 // Return previous one |
| |
1695 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1696 NS_IF_ADDREF(*_oldHandler = mProgressHandler); |
| |
1697 |
| |
1698 if (!aHandler || aGranularity <= 0) { |
| |
1699 aHandler = nullptr; |
| |
1700 aGranularity = 0; |
| |
1701 } |
| |
1702 mProgressHandler = aHandler; |
| |
1703 ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this); |
| |
1704 |
| |
1705 return NS_OK; |
| |
1706 } |
| |
1707 |
| |
1708 NS_IMETHODIMP |
| |
1709 Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler) |
| |
1710 { |
| |
1711 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1712 |
| |
1713 // Return previous one |
| |
1714 SQLiteMutexAutoLock lockedScope(sharedDBMutex); |
| |
1715 NS_IF_ADDREF(*_oldHandler = mProgressHandler); |
| |
1716 |
| |
1717 mProgressHandler = nullptr; |
| |
1718 ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr); |
| |
1719 |
| |
1720 return NS_OK; |
| |
1721 } |
| |
1722 |
| |
1723 NS_IMETHODIMP |
| |
1724 Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName) |
| |
1725 { |
| |
1726 // Bug 597215: Disk space is extremely limited on Android |
| |
1727 // so don't preallocate space. This is also not effective |
| |
1728 // on log structured file systems used by Android devices |
| |
1729 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO) |
| |
1730 // Don't preallocate if less than 500MiB is available. |
| |
1731 int64_t bytesAvailable; |
| |
1732 nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable); |
| |
1733 NS_ENSURE_SUCCESS(rv, rv); |
| |
1734 if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) { |
| |
1735 return NS_ERROR_FILE_TOO_BIG; |
| |
1736 } |
| |
1737 |
| |
1738 (void)::sqlite3_file_control(mDBConn, |
| |
1739 aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get() |
| |
1740 : nullptr, |
| |
1741 SQLITE_FCNTL_CHUNK_SIZE, |
| |
1742 &aChunkSize); |
| |
1743 #endif |
| |
1744 return NS_OK; |
| |
1745 } |
| |
1746 |
| |
1747 NS_IMETHODIMP |
| |
1748 Connection::EnableModule(const nsACString& aModuleName) |
| |
1749 { |
| |
1750 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; |
| |
1751 |
| |
1752 for (size_t i = 0; i < ArrayLength(gModules); i++) { |
| |
1753 struct Module* m = &gModules[i]; |
| |
1754 if (aModuleName.Equals(m->name)) { |
| |
1755 int srv = m->registerFunc(mDBConn, m->name); |
| |
1756 if (srv != SQLITE_OK) |
| |
1757 return convertResultCode(srv); |
| |
1758 |
| |
1759 return NS_OK; |
| |
1760 } |
| |
1761 } |
| |
1762 |
| |
1763 return NS_ERROR_FAILURE; |
| |
1764 } |
| |
1765 |
| |
1766 } // namespace storage |
| |
1767 } // namespace mozilla |