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
16 * The Original Code is Oracle Corporation code.
18 * The Initial Developer of the Original Code is
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
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 ***** */
47 #include "nsIMutableArray.h"
48 #include "nsHashSets.h"
49 #include "nsAutoPtr.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"
74 PRLogModuleInfo
* gStorageLog
= nsnull
;
82 ////////////////////////////////////////////////////////////////////////////////
83 //// Variant Specialization Functions (variantToSQLiteT)
86 sqlite3_T_int(sqlite3_context
*aCtx
,
89 ::sqlite3_result_int(aCtx
, aValue
);
94 sqlite3_T_int64(sqlite3_context
*aCtx
,
97 ::sqlite3_result_int64(aCtx
, aValue
);
102 sqlite3_T_double(sqlite3_context
*aCtx
,
105 ::sqlite3_result_double(aCtx
, aValue
);
110 sqlite3_T_text(sqlite3_context
*aCtx
,
111 const nsCString
&aValue
)
113 ::sqlite3_result_text(aCtx
,
121 sqlite3_T_text16(sqlite3_context
*aCtx
,
122 const nsString
&aValue
)
124 ::sqlite3_result_text16(aCtx
,
126 aValue
.Length() * 2, // Number of bytes.
132 sqlite3_T_null(sqlite3_context
*aCtx
)
134 ::sqlite3_result_null(aCtx
);
139 sqlite3_T_blob(sqlite3_context
*aCtx
,
143 ::sqlite3_result_blob(aCtx
, aData
, aSize
, NS_Free
);
147 #include "variantToSQLiteT_impl.h"
149 ////////////////////////////////////////////////////////////////////////////////
153 void tracefunc (void *aClosure
, const char *aStmt
)
155 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("sqlite3_trace on %p for '%s'", aClosure
,
166 findFunctionEnumerator(const nsACString
&aKey
,
167 Connection::FunctionInfo aData
,
170 FFEArguments
*args
= static_cast<FFEArguments
*>(aUserArg
);
171 if (aData
.function
== args
->target
) {
173 return PL_DHASH_STOP
;
175 return PL_DHASH_NEXT
;
179 copyFunctionEnumerator(const nsACString
&aKey
,
180 Connection::FunctionInfo aData
,
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
);
194 mozIStorageAggregateFunction
*function
=
195 static_cast<mozIStorageAggregateFunction
*>(aData
.function
.get());
196 (void)connection
->CreateAggregateFunction(aKey
, aData
.numArgs
, function
);
199 return PL_DHASH_NEXT
;
203 basicFunctionHelper(sqlite3_context
*aCtx
,
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
));
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",
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",
232 aggregateFunctionStepHelper(sqlite3_context
*aCtx
,
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
));
244 if (NS_FAILED(func
->OnStep(arguments
)))
245 NS_WARNING("User aggregate step function returned error code!");
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",
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",
272 } // anonymous namespace
274 ////////////////////////////////////////////////////////////////////////////////
279 class AsyncCloseConnection
: public nsRunnable
282 AsyncCloseConnection(Connection
*aConnection
,
283 nsIEventTarget
*aCallingThread
,
284 nsIRunnable
*aCallbackEvent
)
285 : mConnection(aConnection
)
286 , mCallingThread(aCallingThread
)
287 , mCallbackEvent(aCallbackEvent
)
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
);
303 (void)mConnection
->internalClose();
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
;
322 nsRefPtr
<Connection
> mConnection
;
323 nsCOMPtr
<nsIEventTarget
> mCallingThread
;
324 nsCOMPtr
<nsIRunnable
> mCallbackEvent
;
327 } // anonymous namespace
329 ////////////////////////////////////////////////////////////////////////////////
332 Connection::Connection(Service
*aService
,
334 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
335 , sharedDBMutex("Connection::sharedDBMutex")
336 , threadOpenedOn(do_GetCurrentThread())
338 , mAsyncExecutionThreadShuttingDown(false)
339 , mTransactionInProgress(PR_FALSE
)
340 , mProgressHandler(nsnull
)
342 , mStorageService(aService
)
347 Connection::~Connection()
352 NS_IMPL_THREADSAFE_ISUPPORTS2(
354 mozIStorageConnection
,
355 nsIInterfaceRequestor
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
)
368 if (!mAsyncExecutionThread
) {
369 nsresult rv
= ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread
));
371 NS_WARNING("Failed to create async thread.");
376 return mAsyncExecutionThread
;
380 Connection::initialize(nsIFile
*aDatabaseFile
,
381 const char* aVFSName
)
383 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
388 mDatabaseFile
= aDatabaseFile
;
392 rv
= aDatabaseFile
->GetPath(path
);
393 NS_ENSURE_SUCCESS(rv
, rv
);
395 srv
= ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path
).get(), &mDBConn
, mFlags
,
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
) {
404 return convertResultCode(srv
);
407 // Properly wrap the database handle's mutex.
408 sharedDBMutex
.initWithMutex(sqlite3_db_mutex(mDBConn
));
412 gStorageLog
= ::PR_NewLogModule("mozStorage");
414 ::sqlite3_trace(mDBConn
, tracefunc
, this);
416 nsCAutoString
leafName(":memory");
418 (void)aDatabaseFile
->GetNativeLeafName(leafName
);
419 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Opening connection to '%s' (%p)",
420 leafName
.get(), this));
422 // Switch db to preferred page size in case the user vacuums.
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
);
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
);
445 return convertResultCode(srv
);
448 // Execute a dummy statement to force the db open, and to verify if it is
450 srv
= prepareStmt(mDBConn
, NS_LITERAL_CSTRING("SELECT * FROM sqlite_master"),
452 if (srv
== SQLITE_OK
) {
453 srv
= stepStmt(stmt
);
455 if (srv
== SQLITE_DONE
|| srv
== SQLITE_ROW
)
457 ::sqlite3_finalize(stmt
);
460 if (srv
!= SQLITE_OK
) {
461 ::sqlite3_close(mDBConn
);
464 return convertResultCode(srv
);
467 // Set the synchronous PRAGMA, according to the preference.
468 switch (Service::getSynchronousPref()) {
470 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
471 "PRAGMA synchronous = FULL;"));
474 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
475 "PRAGMA synchronous = OFF;"));
479 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
480 "PRAGMA synchronous = NORMAL;"));
488 Connection::databaseElementExists(enum DatabaseElementType aElementType
,
489 const nsACString
&aElementName
,
492 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
494 nsCAutoString
query("SELECT name FROM sqlite_master WHERE type = '");
495 switch (aElementType
) {
497 query
.Append("index");
500 query
.Append("table");
503 query
.Append("' AND name ='");
504 query
.Append(aElementName
);
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
) {
520 if (srv
== SQLITE_DONE
) {
525 return convertResultCode(srv
);
529 Connection::findFunctionByInstance(nsISupports
*aInstance
)
531 sharedDBMutex
.assertCurrentThreadOwns();
532 FFEArguments args
= { aInstance
, false };
533 (void)mFunctions
.EnumerateRead(findFunctionEnumerator
, &args
);
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
) {
550 nsresult rv
= mProgressHandler
->OnProgress(this, &result
);
551 if (NS_FAILED(rv
)) return 0; // Don't break request
552 return result
? 1 : 0;
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;
581 Connection::internalClose()
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!");
602 nsCAutoString
leafName(":memory");
604 (void)mDatabaseFile
->GetNativeLeafName(leafName
);
605 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Closing connection to '%s'",
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
));
616 ::PR_smprintf_free(msg
);
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!");
625 return convertResultCode(srv
);
629 Connection::getFilename()
631 nsCString
leafname(":memory:");
633 (void)mDatabaseFile
->GetNativeLeafName(leafname
);
638 ////////////////////////////////////////////////////////////////////////////////
639 //// nsIInterfaceRequestor
642 Connection::GetInterface(const nsIID
&aIID
,
645 if (aIID
.Equals(NS_GET_IID(nsIEventTarget
))) {
646 nsIEventTarget
*background
= getAsyncExecutionTarget();
647 NS_IF_ADDREF(background
);
648 *_result
= background
;
651 return NS_ERROR_NO_INTERFACE
;
654 ////////////////////////////////////////////////////////////////////////////////
655 //// mozIStorageConnection
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();
675 Connection::AsyncClose(mozIStorageCompletionCallback
*aCallback
)
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
;
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
);
705 Connection::Clone(PRBool aReadOnly
,
706 mozIStorageConnection
**_connection
)
709 return NS_ERROR_NOT_INITIALIZED
;
711 return NS_ERROR_UNEXPECTED
;
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
);
734 Connection::GetConnectionReady(PRBool
*_ready
)
736 *_ready
= (mDBConn
!= nsnull
);
741 Connection::GetDatabaseFile(nsIFile
**_dbFile
)
743 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
745 NS_IF_ADDREF(*_dbFile
= mDatabaseFile
);
751 Connection::GetLastInsertRowID(PRInt64
*_id
)
753 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
755 sqlite_int64 id
= ::sqlite3_last_insert_rowid(mDBConn
);
762 Connection::GetLastError(PRInt32
*_error
)
764 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
766 *_error
= ::sqlite3_errcode(mDBConn
);
772 Connection::GetLastErrorString(nsACString
&_errorString
)
774 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
776 const char *serr
= ::sqlite3_errmsg(mDBConn
);
777 _errorString
.Assign(serr
);
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
);
794 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
)
795 *_version
= stmt
->AsInt32(0);
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
);
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
);
825 statement
.forget(&rawPtr
);
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
);
850 Connection::ExecuteSimpleSQL(const nsACString
&aSQLStatement
)
852 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
854 int srv
= ::sqlite3_exec(mDBConn
, PromiseFlatCString(aSQLStatement
).get(),
856 return convertResultCode(srv
);
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.
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
);
887 Connection::TableExists(const nsACString
&aTableName
,
890 return databaseElementExists(TABLE
, aTableName
, _exists
);
894 Connection::IndexExists(const nsACString
&aIndexName
,
897 return databaseElementExists(INDEX
, aIndexName
, _exists
);
901 Connection::GetTransactionInProgress(PRBool
*_inProgress
)
903 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
905 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
906 *_inProgress
= mTransactionInProgress
;
911 Connection::BeginTransaction()
913 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED
);
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
;
925 switch(aTransactionType
) {
926 case TRANSACTION_DEFERRED
:
927 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
929 case TRANSACTION_IMMEDIATE
:
930 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
932 case TRANSACTION_EXCLUSIVE
:
933 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
936 return NS_ERROR_ILLEGAL_VALUE
;
938 if (NS_SUCCEEDED(rv
))
939 mTransactionInProgress
= PR_TRUE
;
944 Connection::CommitTransaction()
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
;
960 Connection::RollbackTransaction()
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
;
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
);
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
);
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(),
1008 basicFunctionHelper
,
1011 if (srv
!= SQLITE_OK
)
1012 return convertResultCode(srv
);
1014 FunctionInfo info
= { aFunction
,
1015 Connection::FunctionInfo::SIMPLE
,
1017 NS_ENSURE_TRUE(mFunctions
.Put(aFunctionName
, info
),
1018 NS_ERROR_OUT_OF_MEMORY
);
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(),
1045 aggregateFunctionStepHelper
,
1046 aggregateFunctionFinalHelper
);
1047 if (srv
!= SQLITE_OK
)
1048 return convertResultCode(srv
);
1050 FunctionInfo info
= { aFunction
,
1051 Connection::FunctionInfo::AGGREGATE
,
1053 NS_ENSURE_TRUE(mFunctions
.Put(aFunctionName
, info
),
1054 NS_ERROR_OUT_OF_MEMORY
);
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(),
1075 if (srv
!= SQLITE_OK
)
1076 return convertResultCode(srv
);
1078 mFunctions
.Remove(aFunctionName
);
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) {
1098 mProgressHandler
= aHandler
;
1099 ::sqlite3_progress_handler(mDBConn
, aGranularity
, sProgressHelper
, this);
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
);
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
,
1134 } // namespace storage
1135 } // namespace mozilla