Bug 628949 - Update visible region / glass regions after we paint. r=roc a=2.0.
[mozilla-central.git] / storage / src / mozStorageConnection.cpp
blob6a12f23eeff7ed9559c38836e358665c3a4dd4d2
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 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Oracle Corporation code.
18 * The Initial Developer of the Original Code is
19 * Oracle Corporation
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
25 * Brett Wilson <brettw@gmail.com>
26 * Shawn Wilsher <me@shawnwilsher.com>
27 * Lev Serebryakov <lev@serebryakov.spb.ru>
28 * Drew Willcoxon <adw@mozilla.com>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either the GNU General Public License Version 2 or later (the "GPL"), or
32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 #include <stdio.h>
46 #include "nsError.h"
47 #include "nsIMutableArray.h"
48 #include "nsHashSets.h"
49 #include "nsAutoPtr.h"
50 #include "nsIFile.h"
51 #include "nsThreadUtils.h"
52 #include "nsAutoLock.h"
54 #include "mozIStorageAggregateFunction.h"
55 #include "mozIStorageCompletionCallback.h"
56 #include "mozIStorageFunction.h"
58 #include "mozStorageAsyncStatementExecution.h"
59 #include "mozStorageSQLFunctions.h"
60 #include "mozStorageConnection.h"
61 #include "mozStorageService.h"
62 #include "mozStorageStatement.h"
63 #include "mozStorageAsyncStatement.h"
64 #include "mozStorageArgValueArray.h"
65 #include "mozStoragePrivateHelpers.h"
66 #include "mozStorageStatementData.h"
67 #include "StorageBaseStatementInternal.h"
68 #include "SQLCollations.h"
70 #include "prlog.h"
71 #include "prprf.h"
73 #ifdef PR_LOGGING
74 PRLogModuleInfo* gStorageLog = nsnull;
75 #endif
77 namespace mozilla {
78 namespace storage {
80 namespace {
82 ////////////////////////////////////////////////////////////////////////////////
83 //// Variant Specialization Functions (variantToSQLiteT)
85 int
86 sqlite3_T_int(sqlite3_context *aCtx,
87 int aValue)
89 ::sqlite3_result_int(aCtx, aValue);
90 return SQLITE_OK;
93 int
94 sqlite3_T_int64(sqlite3_context *aCtx,
95 sqlite3_int64 aValue)
97 ::sqlite3_result_int64(aCtx, aValue);
98 return SQLITE_OK;
102 sqlite3_T_double(sqlite3_context *aCtx,
103 double aValue)
105 ::sqlite3_result_double(aCtx, aValue);
106 return SQLITE_OK;
110 sqlite3_T_text(sqlite3_context *aCtx,
111 const nsCString &aValue)
113 ::sqlite3_result_text(aCtx,
114 aValue.get(),
115 aValue.Length(),
116 SQLITE_TRANSIENT);
117 return SQLITE_OK;
121 sqlite3_T_text16(sqlite3_context *aCtx,
122 const nsString &aValue)
124 ::sqlite3_result_text16(aCtx,
125 aValue.get(),
126 aValue.Length() * 2, // Number of bytes.
127 SQLITE_TRANSIENT);
128 return SQLITE_OK;
132 sqlite3_T_null(sqlite3_context *aCtx)
134 ::sqlite3_result_null(aCtx);
135 return SQLITE_OK;
139 sqlite3_T_blob(sqlite3_context *aCtx,
140 const void *aData,
141 int aSize)
143 ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
144 return SQLITE_OK;
147 #include "variantToSQLiteT_impl.h"
149 ////////////////////////////////////////////////////////////////////////////////
150 //// Local Functions
152 #ifdef PR_LOGGING
153 void tracefunc (void *aClosure, const char *aStmt)
155 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
156 aStmt));
158 #endif
160 struct FFEArguments
162 nsISupports *target;
163 bool found;
165 PLDHashOperator
166 findFunctionEnumerator(const nsACString &aKey,
167 Connection::FunctionInfo aData,
168 void *aUserArg)
170 FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
171 if (aData.function == args->target) {
172 args->found = true;
173 return PL_DHASH_STOP;
175 return PL_DHASH_NEXT;
178 PLDHashOperator
179 copyFunctionEnumerator(const nsACString &aKey,
180 Connection::FunctionInfo aData,
181 void *aUserArg)
183 NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE ||
184 aData.type == Connection::FunctionInfo::AGGREGATE,
185 "Invalid function type!");
187 Connection *connection = static_cast<Connection *>(aUserArg);
188 if (aData.type == Connection::FunctionInfo::SIMPLE) {
189 mozIStorageFunction *function =
190 static_cast<mozIStorageFunction *>(aData.function.get());
191 (void)connection->CreateFunction(aKey, aData.numArgs, function);
193 else {
194 mozIStorageAggregateFunction *function =
195 static_cast<mozIStorageAggregateFunction *>(aData.function.get());
196 (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
199 return PL_DHASH_NEXT;
202 void
203 basicFunctionHelper(sqlite3_context *aCtx,
204 int aArgc,
205 sqlite3_value **aArgv)
207 void *userData = ::sqlite3_user_data(aCtx);
209 mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
211 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
212 if (!arguments)
213 return;
215 nsCOMPtr<nsIVariant> result;
216 if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) {
217 NS_WARNING("User function returned error code!");
218 ::sqlite3_result_error(aCtx,
219 "User function returned error code",
220 -1);
221 return;
223 if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
224 NS_WARNING("User function returned invalid data type!");
225 ::sqlite3_result_error(aCtx,
226 "User function returned invalid data type",
227 -1);
231 void
232 aggregateFunctionStepHelper(sqlite3_context *aCtx,
233 int aArgc,
234 sqlite3_value **aArgv)
236 void *userData = ::sqlite3_user_data(aCtx);
237 mozIStorageAggregateFunction *func =
238 static_cast<mozIStorageAggregateFunction *>(userData);
240 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
241 if (!arguments)
242 return;
244 if (NS_FAILED(func->OnStep(arguments)))
245 NS_WARNING("User aggregate step function returned error code!");
248 void
249 aggregateFunctionFinalHelper(sqlite3_context *aCtx)
251 void *userData = ::sqlite3_user_data(aCtx);
252 mozIStorageAggregateFunction *func =
253 static_cast<mozIStorageAggregateFunction *>(userData);
255 nsRefPtr<nsIVariant> result;
256 if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
257 NS_WARNING("User aggregate final function returned error code!");
258 ::sqlite3_result_error(aCtx,
259 "User aggregate final function returned error code",
260 -1);
261 return;
264 if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
265 NS_WARNING("User aggregate final function returned invalid data type!");
266 ::sqlite3_result_error(aCtx,
267 "User aggregate final function returned invalid data type",
268 -1);
272 } // anonymous namespace
274 ////////////////////////////////////////////////////////////////////////////////
275 //// Local Classes
277 namespace {
279 class AsyncCloseConnection : public nsRunnable
281 public:
282 AsyncCloseConnection(Connection *aConnection,
283 nsIEventTarget *aCallingThread,
284 nsIRunnable *aCallbackEvent)
285 : mConnection(aConnection)
286 , mCallingThread(aCallingThread)
287 , mCallbackEvent(aCallbackEvent)
291 NS_METHOD Run()
293 // This event is first dispatched to the background thread to ensure that
294 // all pending asynchronous events are completed, and then back to the
295 // calling thread to actually close and notify.
296 PRBool onCallingThread = PR_FALSE;
297 (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
298 if (!onCallingThread) {
299 (void)mCallingThread->Dispatch(this, NS_DISPATCH_NORMAL);
300 return NS_OK;
303 (void)mConnection->internalClose();
304 if (mCallbackEvent)
305 (void)mCallingThread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
307 // Because we have no guarantee that the invocation of this method on the
308 // asynchronous thread has fully completed (including the Release of the
309 // reference to this object held by that event loop), we need to explicitly
310 // null out our pointers here. It is possible this object will be destroyed
311 // on the asynchronous thread and if the references are still alive we will
312 // release them on that thread. We definitely do not want that for
313 // mConnection and it's nice to avoid for mCallbackEvent too. We do not
314 // null out mCallingThread because it is conceivable the async thread might
315 // still be 'in' the object.
316 mConnection = nsnull;
317 mCallbackEvent = nsnull;
319 return NS_OK;
321 private:
322 nsRefPtr<Connection> mConnection;
323 nsCOMPtr<nsIEventTarget> mCallingThread;
324 nsCOMPtr<nsIRunnable> mCallbackEvent;
327 } // anonymous namespace
329 ////////////////////////////////////////////////////////////////////////////////
330 //// Connection
332 Connection::Connection(Service *aService,
333 int aFlags)
334 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
335 , sharedDBMutex("Connection::sharedDBMutex")
336 , threadOpenedOn(do_GetCurrentThread())
337 , mDBConn(nsnull)
338 , mAsyncExecutionThreadShuttingDown(false)
339 , mTransactionInProgress(PR_FALSE)
340 , mProgressHandler(nsnull)
341 , mFlags(aFlags)
342 , mStorageService(aService)
344 mFunctions.Init();
347 Connection::~Connection()
349 (void)Close();
352 NS_IMPL_THREADSAFE_ISUPPORTS2(
353 Connection,
354 mozIStorageConnection,
355 nsIInterfaceRequestor
358 nsIEventTarget *
359 Connection::getAsyncExecutionTarget()
361 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
363 // If we are shutting down the asynchronous thread, don't hand out any more
364 // references to the thread.
365 if (mAsyncExecutionThreadShuttingDown)
366 return nsnull;
368 if (!mAsyncExecutionThread) {
369 nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
370 if (NS_FAILED(rv)) {
371 NS_WARNING("Failed to create async thread.");
372 return nsnull;
376 return mAsyncExecutionThread;
379 nsresult
380 Connection::initialize(nsIFile *aDatabaseFile,
381 const char* aVFSName)
383 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
385 int srv;
386 nsresult rv;
388 mDatabaseFile = aDatabaseFile;
390 if (aDatabaseFile) {
391 nsAutoString path;
392 rv = aDatabaseFile->GetPath(path);
393 NS_ENSURE_SUCCESS(rv, rv);
395 srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
396 aVFSName);
398 else {
399 // in memory database requested, sqlite uses a magic file name
400 srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, aVFSName);
402 if (srv != SQLITE_OK) {
403 mDBConn = nsnull;
404 return convertResultCode(srv);
407 // Properly wrap the database handle's mutex.
408 sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
410 #ifdef PR_LOGGING
411 if (!gStorageLog)
412 gStorageLog = ::PR_NewLogModule("mozStorage");
414 ::sqlite3_trace(mDBConn, tracefunc, this);
416 nsCAutoString leafName(":memory");
417 if (aDatabaseFile)
418 (void)aDatabaseFile->GetNativeLeafName(leafName);
419 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
420 leafName.get(), this));
421 #endif
422 // Switch db to preferred page size in case the user vacuums.
423 sqlite3_stmt *stmt;
424 nsCAutoString pageSizeQuery(NS_LITERAL_CSTRING("PRAGMA page_size = "));
425 pageSizeQuery.AppendInt(DEFAULT_PAGE_SIZE);
426 srv = prepareStmt(mDBConn, pageSizeQuery, &stmt);
427 if (srv == SQLITE_OK) {
428 (void)stepStmt(stmt);
429 (void)::sqlite3_finalize(stmt);
432 // Register our built-in SQL functions.
433 srv = registerFunctions(mDBConn);
434 if (srv != SQLITE_OK) {
435 ::sqlite3_close(mDBConn);
436 mDBConn = nsnull;
437 return convertResultCode(srv);
440 // Register our built-in SQL collating sequences.
441 srv = registerCollations(mDBConn, mStorageService);
442 if (srv != SQLITE_OK) {
443 ::sqlite3_close(mDBConn);
444 mDBConn = nsnull;
445 return convertResultCode(srv);
448 // Execute a dummy statement to force the db open, and to verify if it is
449 // valid or not.
450 srv = prepareStmt(mDBConn, NS_LITERAL_CSTRING("SELECT * FROM sqlite_master"),
451 &stmt);
452 if (srv == SQLITE_OK) {
453 srv = stepStmt(stmt);
455 if (srv == SQLITE_DONE || srv == SQLITE_ROW)
456 srv = SQLITE_OK;
457 ::sqlite3_finalize(stmt);
460 if (srv != SQLITE_OK) {
461 ::sqlite3_close(mDBConn);
462 mDBConn = nsnull;
464 return convertResultCode(srv);
467 // Set the synchronous PRAGMA, according to the preference.
468 switch (Service::getSynchronousPref()) {
469 case 2:
470 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
471 "PRAGMA synchronous = FULL;"));
472 break;
473 case 0:
474 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
475 "PRAGMA synchronous = OFF;"));
476 break;
477 case 1:
478 default:
479 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
480 "PRAGMA synchronous = NORMAL;"));
481 break;
484 return NS_OK;
487 nsresult
488 Connection::databaseElementExists(enum DatabaseElementType aElementType,
489 const nsACString &aElementName,
490 PRBool *_exists)
492 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
494 nsCAutoString query("SELECT name FROM sqlite_master WHERE type = '");
495 switch (aElementType) {
496 case INDEX:
497 query.Append("index");
498 break;
499 case TABLE:
500 query.Append("table");
501 break;
503 query.Append("' AND name ='");
504 query.Append(aElementName);
505 query.Append("'");
507 sqlite3_stmt *stmt;
508 int srv = prepareStmt(mDBConn, query, &stmt);
509 if (srv != SQLITE_OK)
510 return convertResultCode(srv);
512 srv = stepStmt(stmt);
513 // we just care about the return value from step
514 (void)::sqlite3_finalize(stmt);
516 if (srv == SQLITE_ROW) {
517 *_exists = PR_TRUE;
518 return NS_OK;
520 if (srv == SQLITE_DONE) {
521 *_exists = PR_FALSE;
522 return NS_OK;
525 return convertResultCode(srv);
528 bool
529 Connection::findFunctionByInstance(nsISupports *aInstance)
531 sharedDBMutex.assertCurrentThreadOwns();
532 FFEArguments args = { aInstance, false };
533 (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
534 return args.found;
537 /* static */ int
538 Connection::sProgressHelper(void *aArg)
540 Connection *_this = static_cast<Connection *>(aArg);
541 return _this->progressHandler();
545 Connection::progressHandler()
547 sharedDBMutex.assertCurrentThreadOwns();
548 if (mProgressHandler) {
549 PRBool result;
550 nsresult rv = mProgressHandler->OnProgress(this, &result);
551 if (NS_FAILED(rv)) return 0; // Don't break request
552 return result ? 1 : 0;
554 return 0;
557 nsresult
558 Connection::setClosedState()
560 // Ensure that we are on the correct thread to close the database.
561 PRBool onOpenedThread;
562 nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
563 NS_ENSURE_SUCCESS(rv, rv);
564 if (!onOpenedThread) {
565 NS_ERROR("Must close the database on the thread that you opened it with!");
566 return NS_ERROR_UNEXPECTED;
569 // Flag that we are shutting down the async thread, so that
570 // getAsyncExecutionTarget knows not to expose/create the async thread.
572 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
573 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
574 mAsyncExecutionThreadShuttingDown = true;
577 return NS_OK;
580 nsresult
581 Connection::internalClose()
583 #ifdef DEBUG
584 // Sanity checks to make sure we are in the proper state before calling this.
585 NS_ASSERTION(mDBConn, "Database connection is already null!");
587 { // Make sure we have marked our async thread as shutting down.
588 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
589 NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
590 "Did not call setClosedState!");
593 { // Ensure that we are being called on the thread we were opened with.
594 PRBool onOpenedThread = PR_FALSE;
595 (void)threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
596 NS_ASSERTION(onOpenedThread,
597 "Not called on the thread the database was opened on!");
599 #endif
601 #ifdef PR_LOGGING
602 nsCAutoString leafName(":memory");
603 if (mDatabaseFile)
604 (void)mDatabaseFile->GetNativeLeafName(leafName);
605 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
606 leafName.get()));
607 #endif
609 #ifdef DEBUG
610 // Notify about any non-finalized statements.
611 sqlite3_stmt *stmt = NULL;
612 while ((stmt = ::sqlite3_next_stmt(mDBConn, stmt))) {
613 char *msg = ::PR_smprintf("SQL statement '%s' was not finalized",
614 ::sqlite3_sql(stmt));
615 NS_WARNING(msg);
616 ::PR_smprintf_free(msg);
618 #endif
620 int srv = ::sqlite3_close(mDBConn);
621 NS_ASSERTION(srv == SQLITE_OK,
622 "sqlite3_close failed. There are probably outstanding statements that are listed above!");
624 mDBConn = NULL;
625 return convertResultCode(srv);
628 nsCString
629 Connection::getFilename()
631 nsCString leafname(":memory:");
632 if (mDatabaseFile) {
633 (void)mDatabaseFile->GetNativeLeafName(leafname);
635 return leafname;
638 ////////////////////////////////////////////////////////////////////////////////
639 //// nsIInterfaceRequestor
641 NS_IMETHODIMP
642 Connection::GetInterface(const nsIID &aIID,
643 void **_result)
645 if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
646 nsIEventTarget *background = getAsyncExecutionTarget();
647 NS_IF_ADDREF(background);
648 *_result = background;
649 return NS_OK;
651 return NS_ERROR_NO_INTERFACE;
654 ////////////////////////////////////////////////////////////////////////////////
655 //// mozIStorageConnection
657 NS_IMETHODIMP
658 Connection::Close()
660 if (!mDBConn)
661 return NS_ERROR_NOT_INITIALIZED;
663 { // Make sure we have not executed any asynchronous statements.
664 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
665 NS_ENSURE_FALSE(mAsyncExecutionThread, NS_ERROR_UNEXPECTED);
668 nsresult rv = setClosedState();
669 NS_ENSURE_SUCCESS(rv, rv);
671 return internalClose();
674 NS_IMETHODIMP
675 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
677 if (!mDBConn)
678 return NS_ERROR_NOT_INITIALIZED;
680 nsIEventTarget *asyncThread = getAsyncExecutionTarget();
681 NS_ENSURE_TRUE(asyncThread, NS_ERROR_UNEXPECTED);
683 nsresult rv = setClosedState();
684 NS_ENSURE_SUCCESS(rv, rv);
686 // Create our callback event if we were given a callback.
687 nsCOMPtr<nsIRunnable> completeEvent;
688 if (aCallback) {
689 completeEvent = newCompletionEvent(aCallback);
690 NS_ENSURE_TRUE(completeEvent, NS_ERROR_OUT_OF_MEMORY);
693 // Create and dispatch our close event to the background thread.
694 nsCOMPtr<nsIRunnable> closeEvent =
695 new AsyncCloseConnection(this, NS_GetCurrentThread(), completeEvent);
696 NS_ENSURE_TRUE(closeEvent, NS_ERROR_OUT_OF_MEMORY);
698 rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
699 NS_ENSURE_SUCCESS(rv, rv);
701 return NS_OK;
704 NS_IMETHODIMP
705 Connection::Clone(PRBool aReadOnly,
706 mozIStorageConnection **_connection)
708 if (!mDBConn)
709 return NS_ERROR_NOT_INITIALIZED;
710 if (!mDatabaseFile)
711 return NS_ERROR_UNEXPECTED;
713 int flags = mFlags;
714 if (aReadOnly) {
715 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
716 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
717 // Turn off SQLITE_OPEN_CREATE.
718 flags = (~SQLITE_OPEN_CREATE & flags);
720 nsRefPtr<Connection> clone = new Connection(mStorageService, flags);
721 NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
723 nsresult rv = clone->initialize(mDatabaseFile);
724 NS_ENSURE_SUCCESS(rv, rv);
726 // Copy any functions that have been added to this connection.
727 (void)mFunctions.EnumerateRead(copyFunctionEnumerator, clone);
729 NS_ADDREF(*_connection = clone);
730 return NS_OK;
733 NS_IMETHODIMP
734 Connection::GetConnectionReady(PRBool *_ready)
736 *_ready = (mDBConn != nsnull);
737 return NS_OK;
740 NS_IMETHODIMP
741 Connection::GetDatabaseFile(nsIFile **_dbFile)
743 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
745 NS_IF_ADDREF(*_dbFile = mDatabaseFile);
747 return NS_OK;
750 NS_IMETHODIMP
751 Connection::GetLastInsertRowID(PRInt64 *_id)
753 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
755 sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
756 *_id = id;
758 return NS_OK;
761 NS_IMETHODIMP
762 Connection::GetLastError(PRInt32 *_error)
764 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
766 *_error = ::sqlite3_errcode(mDBConn);
768 return NS_OK;
771 NS_IMETHODIMP
772 Connection::GetLastErrorString(nsACString &_errorString)
774 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
776 const char *serr = ::sqlite3_errmsg(mDBConn);
777 _errorString.Assign(serr);
779 return NS_OK;
782 NS_IMETHODIMP
783 Connection::GetSchemaVersion(PRInt32 *_version)
785 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
787 nsCOMPtr<mozIStorageStatement> stmt;
788 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
789 getter_AddRefs(stmt));
790 NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
792 *_version = 0;
793 PRBool hasResult;
794 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
795 *_version = stmt->AsInt32(0);
797 return NS_OK;
800 NS_IMETHODIMP
801 Connection::SetSchemaVersion(PRInt32 aVersion)
803 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
805 nsCAutoString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
806 stmt.AppendInt(aVersion);
808 return ExecuteSimpleSQL(stmt);
811 NS_IMETHODIMP
812 Connection::CreateStatement(const nsACString &aSQLStatement,
813 mozIStorageStatement **_stmt)
815 NS_ENSURE_ARG_POINTER(_stmt);
816 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
818 nsRefPtr<Statement> statement(new Statement());
819 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
821 nsresult rv = statement->initialize(this, aSQLStatement);
822 NS_ENSURE_SUCCESS(rv, rv);
824 Statement *rawPtr;
825 statement.forget(&rawPtr);
826 *_stmt = rawPtr;
827 return NS_OK;
830 NS_IMETHODIMP
831 Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
832 mozIStorageAsyncStatement **_stmt)
834 NS_ENSURE_ARG_POINTER(_stmt);
835 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
837 nsRefPtr<AsyncStatement> statement(new AsyncStatement());
838 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
840 nsresult rv = statement->initialize(this, aSQLStatement);
841 NS_ENSURE_SUCCESS(rv, rv);
843 AsyncStatement *rawPtr;
844 statement.forget(&rawPtr);
845 *_stmt = rawPtr;
846 return NS_OK;
849 NS_IMETHODIMP
850 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
852 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
854 int srv = ::sqlite3_exec(mDBConn, PromiseFlatCString(aSQLStatement).get(),
855 NULL, NULL, NULL);
856 return convertResultCode(srv);
859 NS_IMETHODIMP
860 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
861 PRUint32 aNumStatements,
862 mozIStorageStatementCallback *aCallback,
863 mozIStoragePendingStatement **_handle)
865 nsTArray<StatementData> stmts(aNumStatements);
866 for (PRUint32 i = 0; i < aNumStatements; i++) {
867 nsCOMPtr<StorageBaseStatementInternal> stmt =
868 do_QueryInterface(aStatements[i]);
870 // Obtain our StatementData.
871 StatementData data;
872 nsresult rv = stmt->getAsynchronousStatementData(data);
873 NS_ENSURE_SUCCESS(rv, rv);
875 NS_ASSERTION(stmt->getOwner() == this,
876 "Statement must be from this database connection!");
878 // Now append it to our array.
879 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
882 // Dispatch to the background
883 return AsyncExecuteStatements::execute(stmts, this, aCallback, _handle);
886 NS_IMETHODIMP
887 Connection::TableExists(const nsACString &aTableName,
888 PRBool *_exists)
890 return databaseElementExists(TABLE, aTableName, _exists);
893 NS_IMETHODIMP
894 Connection::IndexExists(const nsACString &aIndexName,
895 PRBool* _exists)
897 return databaseElementExists(INDEX, aIndexName, _exists);
900 NS_IMETHODIMP
901 Connection::GetTransactionInProgress(PRBool *_inProgress)
903 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
905 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
906 *_inProgress = mTransactionInProgress;
907 return NS_OK;
910 NS_IMETHODIMP
911 Connection::BeginTransaction()
913 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
916 NS_IMETHODIMP
917 Connection::BeginTransactionAs(PRInt32 aTransactionType)
919 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
921 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
922 if (mTransactionInProgress)
923 return NS_ERROR_FAILURE;
924 nsresult rv;
925 switch(aTransactionType) {
926 case TRANSACTION_DEFERRED:
927 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
928 break;
929 case TRANSACTION_IMMEDIATE:
930 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
931 break;
932 case TRANSACTION_EXCLUSIVE:
933 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
934 break;
935 default:
936 return NS_ERROR_ILLEGAL_VALUE;
938 if (NS_SUCCEEDED(rv))
939 mTransactionInProgress = PR_TRUE;
940 return rv;
943 NS_IMETHODIMP
944 Connection::CommitTransaction()
946 if (!mDBConn)
947 return NS_ERROR_NOT_INITIALIZED;
949 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
950 if (!mTransactionInProgress)
951 return NS_ERROR_UNEXPECTED;
953 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
954 if (NS_SUCCEEDED(rv))
955 mTransactionInProgress = PR_FALSE;
956 return rv;
959 NS_IMETHODIMP
960 Connection::RollbackTransaction()
962 if (!mDBConn)
963 return NS_ERROR_NOT_INITIALIZED;
965 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
966 if (!mTransactionInProgress)
967 return NS_ERROR_UNEXPECTED;
969 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
970 if (NS_SUCCEEDED(rv))
971 mTransactionInProgress = PR_FALSE;
972 return rv;
975 NS_IMETHODIMP
976 Connection::CreateTable(const char *aTableName,
977 const char *aTableSchema)
979 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
981 char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
982 if (!buf)
983 return NS_ERROR_OUT_OF_MEMORY;
985 int srv = ::sqlite3_exec(mDBConn, buf, NULL, NULL, NULL);
986 ::PR_smprintf_free(buf);
988 return convertResultCode(srv);
991 NS_IMETHODIMP
992 Connection::CreateFunction(const nsACString &aFunctionName,
993 PRInt32 aNumArguments,
994 mozIStorageFunction *aFunction)
996 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
998 // Check to see if this function is already defined. We only check the name
999 // because a function can be defined with the same body but different names.
1000 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1001 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
1003 int srv = ::sqlite3_create_function(mDBConn,
1004 nsPromiseFlatCString(aFunctionName).get(),
1005 aNumArguments,
1006 SQLITE_ANY,
1007 aFunction,
1008 basicFunctionHelper,
1009 NULL,
1010 NULL);
1011 if (srv != SQLITE_OK)
1012 return convertResultCode(srv);
1014 FunctionInfo info = { aFunction,
1015 Connection::FunctionInfo::SIMPLE,
1016 aNumArguments };
1017 NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info),
1018 NS_ERROR_OUT_OF_MEMORY);
1020 return NS_OK;
1023 NS_IMETHODIMP
1024 Connection::CreateAggregateFunction(const nsACString &aFunctionName,
1025 PRInt32 aNumArguments,
1026 mozIStorageAggregateFunction *aFunction)
1028 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1030 // Check to see if this function name is already defined.
1031 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1032 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
1034 // Because aggregate functions depend on state across calls, you cannot have
1035 // the same instance use the same name. We want to enumerate all functions
1036 // and make sure this instance is not already registered.
1037 NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
1039 int srv = ::sqlite3_create_function(mDBConn,
1040 nsPromiseFlatCString(aFunctionName).get(),
1041 aNumArguments,
1042 SQLITE_ANY,
1043 aFunction,
1044 NULL,
1045 aggregateFunctionStepHelper,
1046 aggregateFunctionFinalHelper);
1047 if (srv != SQLITE_OK)
1048 return convertResultCode(srv);
1050 FunctionInfo info = { aFunction,
1051 Connection::FunctionInfo::AGGREGATE,
1052 aNumArguments };
1053 NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info),
1054 NS_ERROR_OUT_OF_MEMORY);
1056 return NS_OK;
1059 NS_IMETHODIMP
1060 Connection::RemoveFunction(const nsACString &aFunctionName)
1062 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1064 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1065 NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
1067 int srv = ::sqlite3_create_function(mDBConn,
1068 nsPromiseFlatCString(aFunctionName).get(),
1070 SQLITE_ANY,
1071 NULL,
1072 NULL,
1073 NULL,
1074 NULL);
1075 if (srv != SQLITE_OK)
1076 return convertResultCode(srv);
1078 mFunctions.Remove(aFunctionName);
1080 return NS_OK;
1083 NS_IMETHODIMP
1084 Connection::SetProgressHandler(PRInt32 aGranularity,
1085 mozIStorageProgressHandler *aHandler,
1086 mozIStorageProgressHandler **_oldHandler)
1088 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1090 // Return previous one
1091 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1092 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1094 if (!aHandler || aGranularity <= 0) {
1095 aHandler = nsnull;
1096 aGranularity = 0;
1098 mProgressHandler = aHandler;
1099 ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
1101 return NS_OK;
1104 NS_IMETHODIMP
1105 Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
1107 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1109 // Return previous one
1110 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1111 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1113 mProgressHandler = nsnull;
1114 ::sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
1116 return NS_OK;
1119 NS_IMETHODIMP
1120 Connection::SetGrowthIncrement(PRInt32 aChunkSize, const nsACString &aDatabaseName)
1122 // Bug 597215: Disk space is extremely limited on Android
1123 // so don't preallocate space. This is also not effective
1124 // on log structured file systems used by Android devices
1125 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
1126 (void)::sqlite3_file_control(mDBConn,
1127 aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get() : NULL,
1128 SQLITE_FCNTL_CHUNK_SIZE,
1129 &aChunkSize);
1130 #endif
1131 return NS_OK;
1134 } // namespace storage
1135 } // namespace mozilla