|
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 |