1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either the GNU General Public License Version 2 or later (the "GPL"), or
31 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
46 #include "nsIMutableArray.h"
47 #include "nsHashSets.h"
48 #include "nsAutoPtr.h"
50 #include "nsIVariant.h"
51 #include "nsIPrefService.h"
52 #include "nsIPrefBranch.h"
54 #include "mozIStorageAggregateFunction.h"
55 #include "mozIStorageFunction.h"
57 #include "mozStorageEvents.h"
58 #include "mozStorageUnicodeFunctions.h"
59 #include "mozStorageConnection.h"
60 #include "mozStorageService.h"
61 #include "mozStorageStatement.h"
62 #include "mozStorageValueArray.h"
63 #include "mozStorage.h"
69 PRLogModuleInfo
* gStorageLog
= nsnull
;
72 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
74 NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageConnection
, mozIStorageConnection
)
76 mozStorageConnection::mozStorageConnection(mozIStorageService
* aService
) :
78 , mTransactionMutex(nsAutoLock::NewLock("TransactionMutex"))
79 , mTransactionInProgress(PR_FALSE
)
80 , mFunctionsMutex(nsAutoLock::NewLock("FunctionsMutex"))
81 , mProgressHandlerMutex(nsAutoLock::NewLock("ProgressHandlerMutex"))
82 , mProgressHandler(nsnull
)
83 , mStorageService(aService
)
88 mozStorageConnection::~mozStorageConnection()
91 nsAutoLock::DestroyLock(mTransactionMutex
);
92 nsAutoLock::DestroyLock(mFunctionsMutex
);
93 nsAutoLock::DestroyLock(mProgressHandlerMutex
);
97 void tracefunc (void *closure
, const char *stmt
)
99 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("sqlite3_trace on %p for '%s'", closure
,
105 * Actually creates the connection from the DB. Called by mozStorageService.
106 * You can pass a NULL database file in to get an sqlite in-memory database.
109 mozStorageConnection::Initialize(nsIFile
*aDatabaseFile
)
111 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
112 NS_ENSURE_TRUE(mTransactionMutex
, NS_ERROR_OUT_OF_MEMORY
);
113 NS_ENSURE_TRUE(mFunctionsMutex
, NS_ERROR_OUT_OF_MEMORY
);
114 NS_ENSURE_TRUE(mProgressHandlerMutex
, NS_ERROR_OUT_OF_MEMORY
);
119 mDatabaseFile
= aDatabaseFile
;
123 rv
= aDatabaseFile
->GetPath(path
);
124 NS_ENSURE_SUCCESS(rv
, rv
);
126 srv
= sqlite3_open (NS_ConvertUTF16toUTF8(path
).get(), &mDBConn
);
128 // in memory database requested, sqlite uses a magic file name
129 srv
= sqlite3_open (":memory:", &mDBConn
);
131 if (srv
!= SQLITE_OK
) {
133 return ConvertResultCode(srv
);
138 gStorageLog
= PR_NewLogModule("mozStorage");
140 sqlite3_trace (mDBConn
, tracefunc
, this);
142 nsCAutoString
leafName(":memory");
144 (void)aDatabaseFile
->GetNativeLeafName(leafName
);
145 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Opening connection to '%s' (%p)",
146 leafName
.get(), this));
149 // Hook up i18n functions
150 srv
= StorageUnicodeFunctions::RegisterFunctions(mDBConn
);
151 if (srv
!= SQLITE_OK
) {
153 return ConvertResultCode(srv
);
156 /* Execute a dummy statement to force the db open, and to verify
157 * whether it's valid or not
159 sqlite3_stmt
*stmt
= nsnull
;
160 srv
= sqlite3_prepare_v2(mDBConn
, "SELECT * FROM sqlite_master", -1, &stmt
,
163 if (srv
== SQLITE_OK
) {
164 srv
= sqlite3_step(stmt
);
166 if (srv
== SQLITE_DONE
|| srv
== SQLITE_ROW
)
173 sqlite3_finalize (stmt
);
175 if (srv
!= SQLITE_OK
) {
176 sqlite3_close (mDBConn
);
179 return ConvertResultCode(srv
);
182 // Set the synchronous PRAGMA, according to the pref
183 nsCOMPtr
<nsIPrefBranch
> pref(do_GetService(NS_PREFSERVICE_CONTRACTID
));
184 PRInt32 synchronous
= 1; // Default to NORMAL if pref not set
186 (void)pref
->GetIntPref(PREF_TS_SYNCHRONOUS
, &synchronous
);
188 switch (synchronous
) {
190 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
191 "PRAGMA synchronous = FULL;"));
194 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
195 "PRAGMA synchronous = OFF;"));
199 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
200 "PRAGMA synchronous = NORMAL;"));
207 /*****************************************************************************
208 ** mozIStorageConnection interface
209 *****************************************************************************/
212 ** Core status/initialization
216 mozStorageConnection::Close()
219 return NS_ERROR_NOT_INITIALIZED
;
222 nsCAutoString
leafName(":memory");
224 (void)mDatabaseFile
->GetNativeLeafName(leafName
);
225 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Opening connection to '%s'",
230 nsAutoLock
mutex(mProgressHandlerMutex
);
231 if (mProgressHandler
)
232 sqlite3_progress_handler(mDBConn
, 0, NULL
, NULL
);
235 int srv
= sqlite3_close(mDBConn
);
236 if (srv
!= SQLITE_OK
)
237 NS_WARNING("sqlite3_close failed. There are probably outstanding statements!");
240 return ConvertResultCode(srv
);
244 mozStorageConnection::GetConnectionReady(PRBool
*aConnectionReady
)
246 *aConnectionReady
= (mDBConn
!= nsnull
);
251 mozStorageConnection::GetDatabaseFile(nsIFile
**aFile
)
253 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
255 NS_IF_ADDREF(*aFile
= mDatabaseFile
);
261 mozStorageConnection::GetLastInsertRowID(PRInt64
*aLastInsertRowID
)
263 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
265 sqlite_int64 id
= sqlite3_last_insert_rowid(mDBConn
);
266 *aLastInsertRowID
= id
;
272 mozStorageConnection::GetLastError(PRInt32
*aLastError
)
274 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
276 *aLastError
= sqlite3_errcode(mDBConn
);
282 mozStorageConnection::GetLastErrorString(nsACString
& aLastErrorString
)
284 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
286 const char *serr
= sqlite3_errmsg(mDBConn
);
287 aLastErrorString
.Assign(serr
);
293 mozStorageConnection::GetSchemaVersion(PRInt32
*version
)
295 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
297 nsCOMPtr
<mozIStorageStatement
> stmt
;
298 nsresult rv
= CreateStatement(NS_LITERAL_CSTRING(
299 "PRAGMA user_version"), getter_AddRefs(stmt
));
300 if (NS_FAILED(rv
)) return rv
;
304 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
)
305 *version
= stmt
->AsInt32(0);
311 mozStorageConnection::SetSchemaVersion(PRInt32 aVersion
)
313 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
315 nsCAutoString
stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
316 stmt
.AppendInt(aVersion
);
318 return ExecuteSimpleSQL(stmt
);
322 ** Statements & Queries
326 mozStorageConnection::CreateStatement(const nsACString
& aSQLStatement
,
327 mozIStorageStatement
**_retval
)
329 NS_ENSURE_ARG_POINTER(_retval
);
330 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
332 mozStorageStatement
*statement
= new mozStorageStatement();
334 return NS_ERROR_OUT_OF_MEMORY
;
335 NS_ADDREF(statement
);
337 nsresult rv
= statement
->Initialize (this, aSQLStatement
);
339 NS_RELEASE(statement
);
343 *_retval
= statement
;
348 mozStorageConnection::ExecuteSimpleSQL(const nsACString
& aSQLStatement
)
350 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
352 int srv
= sqlite3_exec (mDBConn
, PromiseFlatCString(aSQLStatement
).get(),
354 if (srv
!= SQLITE_OK
) {
355 HandleSqliteError(nsPromiseFlatCString(aSQLStatement
).get());
356 return ConvertResultCode(srv
);
363 mozStorageConnection::ExecuteAsync(mozIStorageStatement
** aStatements
,
364 PRUint32 aNumStatements
,
365 mozIStorageStatementCallback
*aCallback
,
366 mozIStoragePendingStatement
**_stmt
)
369 nsTArray
<sqlite3_stmt
*> stmts(aNumStatements
);
370 for (PRUint32 i
= 0; i
< aNumStatements
&& rc
== SQLITE_OK
; i
++) {
371 sqlite3_stmt
*old_stmt
= aStatements
[i
]->GetNativeStatementPointer();
372 NS_ASSERTION(sqlite3_db_handle(old_stmt
) == mDBConn
,
373 "Statement must be from this database connection!");
375 // Clone this statement. We only need a sqlite3_stmt object, so we can
376 // avoid all the extra work that making a new mozStorageStatement would
377 // normally involve and use the SQLite API.
378 sqlite3_stmt
*new_stmt
;
379 rc
= sqlite3_prepare_v2(mDBConn
, sqlite3_sql(old_stmt
), -1, &new_stmt
,
384 // Transfer the bindings
385 rc
= sqlite3_transfer_bindings(old_stmt
, new_stmt
);
389 if (!stmts
.AppendElement(new_stmt
)) {
395 // Dispatch to the background
398 rv
= NS_executeAsync(stmts
, this, aCallback
, _stmt
);
400 // We had a failure, so we need to clean up...
401 if (rc
!= SQLITE_OK
|| NS_FAILED(rv
)) {
402 for (PRUint32 i
= 0; i
< stmts
.Length(); i
++)
403 (void)sqlite3_finalize(stmts
[i
]);
406 rv
= ConvertResultCode(rc
);
409 // Always reset all the statements
410 for (PRUint32 i
= 0; i
< aNumStatements
; i
++)
411 (void)aStatements
[i
]->Reset();
417 mozStorageConnection::TableExists(const nsACString
& aSQLStatement
, PRBool
*_retval
)
419 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
421 nsCString
query("SELECT name FROM sqlite_master WHERE type = 'table' AND name ='");
422 query
.Append(aSQLStatement
);
423 query
.AppendLiteral("'");
425 sqlite3_stmt
*stmt
= nsnull
;
426 int srv
= sqlite3_prepare_v2(mDBConn
, query
.get(), -1, &stmt
, NULL
);
427 if (srv
!= SQLITE_OK
) {
428 HandleSqliteError(query
.get());
429 return ConvertResultCode(srv
);
432 PRBool exists
= PR_FALSE
;
434 srv
= sqlite3_step(stmt
);
435 // we just care about the return value from step
436 sqlite3_finalize(stmt
);
438 if (srv
== SQLITE_ROW
) {
440 } else if (srv
== SQLITE_DONE
) {
443 HandleSqliteError("TableExists finalize");
444 return ConvertResultCode(srv
);
452 mozStorageConnection::IndexExists(const nsACString
& aIndexName
, PRBool
* _retval
)
454 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
456 nsCString
query("SELECT name FROM sqlite_master WHERE type = 'index' AND name ='");
457 query
.Append(aIndexName
);
458 query
.AppendLiteral("'");
460 sqlite3_stmt
*stmt
= nsnull
;
461 int srv
= sqlite3_prepare_v2(mDBConn
, query
.get(), -1, &stmt
, NULL
);
462 if (srv
!= SQLITE_OK
) {
463 HandleSqliteError(query
.get());
464 return ConvertResultCode(srv
);
469 srv
= sqlite3_step(stmt
);
470 (void)sqlite3_finalize(stmt
);
472 if (srv
== SQLITE_ROW
) {
476 return ConvertResultCode(srv
);
485 mozStorageConnection::GetTransactionInProgress(PRBool
*_retval
)
487 nsAutoLock
mutex(mTransactionMutex
);
488 *_retval
= mTransactionInProgress
;
492 // XXX do we want to just store compiled statements for these?
494 mozStorageConnection::BeginTransaction()
496 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED
);
500 mozStorageConnection::BeginTransactionAs(PRInt32 aTransactionType
)
502 nsAutoLock
mutex(mTransactionMutex
);
503 if (mTransactionInProgress
)
504 return NS_ERROR_FAILURE
;
506 switch(aTransactionType
) {
507 case TRANSACTION_DEFERRED
:
508 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
510 case TRANSACTION_IMMEDIATE
:
511 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
513 case TRANSACTION_EXCLUSIVE
:
514 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
517 return NS_ERROR_ILLEGAL_VALUE
;
519 if (NS_SUCCEEDED(rv
))
520 mTransactionInProgress
= PR_TRUE
;
525 mozStorageConnection::CommitTransaction()
527 nsAutoLock
mutex(mTransactionMutex
);
528 if (!mTransactionInProgress
)
529 return NS_ERROR_FAILURE
;
530 nsresult rv
= ExecuteSimpleSQL (NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
531 // even if the commit fails, the transaction is aborted
532 mTransactionInProgress
= PR_FALSE
;
537 mozStorageConnection::RollbackTransaction()
539 nsAutoLock
mutex(mTransactionMutex
);
540 if (!mTransactionInProgress
)
541 return NS_ERROR_FAILURE
;
542 nsresult rv
= ExecuteSimpleSQL (NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
543 mTransactionInProgress
= PR_FALSE
;
552 mozStorageConnection::CreateTable(/*const nsID& aID,*/
553 const char *aTableName
,
554 const char *aTableSchema
)
556 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
560 buf
= PR_smprintf("CREATE TABLE %s (%s)", aTableName
, aTableSchema
);
562 return NS_ERROR_OUT_OF_MEMORY
;
564 srv
= sqlite3_exec (mDBConn
, buf
,
567 PR_smprintf_free(buf
);
569 return ConvertResultCode(srv
);
577 mozStorageConnection::s_FindFuncEnum(const nsACString
&aKey
,
581 FindFuncEnumArgs
*args
= static_cast<FindFuncEnumArgs
*>(userArg
);
582 if ((void*)aData
== args
->mTarget
) {
583 args
->mFound
= PR_TRUE
;
584 return PL_DHASH_STOP
;
586 return PL_DHASH_NEXT
;
590 mozStorageConnection::FindFunctionByInstance(nsISupports
*aInstance
)
592 // The lock should already be held by calling functions
593 FindFuncEnumArgs args
= { aInstance
, PR_FALSE
};
594 mFunctions
.EnumerateRead(s_FindFuncEnum
, &args
);
599 mozStorageVariantToSQLite3Result(sqlite3_context
*ctx
,
604 // Allow to return NULL not wrapped to
605 // nsIVariant for speed
607 sqlite3_result_null (ctx
);
610 (void)var
->GetDataType( &dt
);
612 case nsIDataType::VTYPE_INT8
:
613 case nsIDataType::VTYPE_INT16
:
614 case nsIDataType::VTYPE_INT32
:
615 case nsIDataType::VTYPE_UINT8
:
616 case nsIDataType::VTYPE_UINT16
:
619 rv
= var
->GetAsInt32 (&v
);
620 if (NS_FAILED(rv
)) return rv
;
621 sqlite3_result_int (ctx
, v
);
624 case nsIDataType::VTYPE_UINT32
: // Try to preserve full range
625 case nsIDataType::VTYPE_INT64
:
626 // Data loss possible, but there is no unsigned types in SQLite
627 case nsIDataType::VTYPE_UINT64
:
630 rv
= var
->GetAsInt64 (&v
);
631 if (NS_FAILED(rv
)) return rv
;
632 sqlite3_result_int64 (ctx
, v
);
635 case nsIDataType::VTYPE_FLOAT
:
636 case nsIDataType::VTYPE_DOUBLE
:
639 rv
= var
->GetAsDouble (&v
);
640 if (NS_FAILED(rv
)) return rv
;
641 sqlite3_result_double (ctx
, v
);
644 case nsIDataType::VTYPE_BOOL
:
647 rv
= var
->GetAsBool(&v
);
648 if (NS_FAILED(rv
)) return rv
;
649 sqlite3_result_int (ctx
, v
? 1 : 0);
652 case nsIDataType::VTYPE_CHAR
:
653 case nsIDataType::VTYPE_WCHAR
:
654 case nsIDataType::VTYPE_DOMSTRING
:
655 case nsIDataType::VTYPE_CHAR_STR
:
656 case nsIDataType::VTYPE_WCHAR_STR
:
657 case nsIDataType::VTYPE_STRING_SIZE_IS
:
658 case nsIDataType::VTYPE_WSTRING_SIZE_IS
:
659 case nsIDataType::VTYPE_UTF8STRING
:
660 case nsIDataType::VTYPE_CSTRING
:
661 case nsIDataType::VTYPE_ASTRING
:
664 // GetAsAString does proper conversion to UCS2
665 // from all string-like types. It can be used
666 // universally without problems.
667 rv
= var
->GetAsAString (v
);
668 if (NS_FAILED(rv
)) return rv
;
669 sqlite3_result_text16 (ctx
,
670 nsPromiseFlatString(v
).get(),
675 case nsIDataType::VTYPE_VOID
:
676 case nsIDataType::VTYPE_EMPTY
:
677 sqlite3_result_null (ctx
);
679 // Maybe, it'll be possible to convert these
681 case nsIDataType::VTYPE_ID
:
682 case nsIDataType::VTYPE_INTERFACE
:
683 case nsIDataType::VTYPE_INTERFACE_IS
:
684 case nsIDataType::VTYPE_ARRAY
:
685 case nsIDataType::VTYPE_EMPTY_ARRAY
:
687 return NS_ERROR_CANNOT_CONVERT_DATA
;
693 mozStorageSqlFuncHelper (sqlite3_context
*ctx
,
695 sqlite3_value
**argv
)
697 void *userData
= sqlite3_user_data (ctx
);
698 // We don't want to QI here, because this will be called a -lot-
699 mozIStorageFunction
*userFunction
=
700 static_cast<mozIStorageFunction
*>(userData
);
702 nsRefPtr
<mozStorageArgvValueArray
> ava
= new mozStorageArgvValueArray (argc
, argv
);
705 nsCOMPtr
<nsIVariant
> retval
;
706 nsresult rv
= userFunction
->OnFunctionCall (ava
, getter_AddRefs(retval
));
708 NS_WARNING("mozIStorageConnection: User function returned error code!\n");
709 sqlite3_result_error(ctx
,
710 "User function returned error code",
714 rv
= mozStorageVariantToSQLite3Result(ctx
,retval
);
716 NS_WARNING("mozIStorageConnection: User function returned invalid data type!\n");
717 sqlite3_result_error(ctx
,
718 "User function returned invalid data type",
724 mozStorageConnection::CreateFunction(const nsACString
&aFunctionName
,
725 PRInt32 aNumArguments
,
726 mozIStorageFunction
*aFunction
)
728 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
730 // do we already have this function defined?
731 // Check for name only because simple function can
732 // be defined multiple times with different names (aliases).
733 nsAutoLock
mutex(mFunctionsMutex
);
734 NS_ENSURE_FALSE(mFunctions
.Get (aFunctionName
, NULL
), NS_ERROR_FAILURE
);
736 int srv
= sqlite3_create_function (mDBConn
,
737 nsPromiseFlatCString(aFunctionName
).get(),
741 mozStorageSqlFuncHelper
,
744 if (srv
!= SQLITE_OK
) {
745 HandleSqliteError(nsnull
);
746 return ConvertResultCode(srv
);
749 if (mFunctions
.Put (aFunctionName
, aFunction
)) {
752 return NS_ERROR_OUT_OF_MEMORY
;
756 mozStorageSqlAggrFuncStepHelper (sqlite3_context
*ctx
,
758 sqlite3_value
**argv
)
760 void *userData
= sqlite3_user_data (ctx
);
761 // We don't want to QI here, because this will be called a -lot-
762 mozIStorageAggregateFunction
*userFunction
=
763 static_cast<mozIStorageAggregateFunction
*>(userData
);
765 nsRefPtr
<mozStorageArgvValueArray
> ava
=
766 new mozStorageArgvValueArray (argc
, argv
);
769 nsresult rv
= userFunction
->OnStep(ava
);
771 NS_WARNING("mozIStorageConnection: User aggregate step function returned error code!\n");
775 mozStorageSqlAggrFuncFinalHelper (sqlite3_context
*ctx
)
777 void *userData
= sqlite3_user_data (ctx
);
778 // We don't want to QI here, because this will be called a -lot-
779 mozIStorageAggregateFunction
*userFunction
=
780 static_cast<mozIStorageAggregateFunction
*>(userData
);
782 nsRefPtr
<nsIVariant
> retval
;
783 nsresult rv
= userFunction
->OnFinal (getter_AddRefs(retval
));
785 NS_WARNING("mozIStorageConnection: User aggregate final function returned error code!\n");
786 sqlite3_result_error(ctx
,
787 "User aggregate final function returned error code",
791 rv
= mozStorageVariantToSQLite3Result(ctx
,retval
);
793 NS_WARNING("mozIStorageConnection: User aggregate final function returned invalid data type!\n");
794 sqlite3_result_error(ctx
,
795 "User aggregate final function returned invalid data type",
801 mozStorageConnection::CreateAggregateFunction(const nsACString
&aFunctionName
,
802 PRInt32 aNumArguments
,
803 mozIStorageAggregateFunction
*aFunction
)
805 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
807 // do we already have this function defined?
809 nsAutoLock
mutex(mFunctionsMutex
);
810 NS_ENSURE_FALSE(mFunctions
.Get (aFunctionName
, NULL
), NS_ERROR_FAILURE
);
812 // Aggregate functions are stateful, so we cannot have
814 // Enumerate all functions and determine if this one is already
816 NS_ENSURE_FALSE(FindFunctionByInstance (aFunction
), NS_ERROR_FAILURE
);
818 int srv
= sqlite3_create_function (mDBConn
,
819 nsPromiseFlatCString(aFunctionName
).get(),
824 mozStorageSqlAggrFuncStepHelper
,
825 mozStorageSqlAggrFuncFinalHelper
);
826 if (srv
!= SQLITE_OK
) {
827 HandleSqliteError(nsnull
);
828 return ConvertResultCode(srv
);
831 if (mFunctions
.Put (aFunctionName
, aFunction
)) {
834 return NS_ERROR_OUT_OF_MEMORY
;
838 mozStorageConnection::RemoveFunction(const nsACString
&aFunctionName
)
840 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
842 nsAutoLock
mutex(mFunctionsMutex
);
843 NS_ENSURE_TRUE(mFunctions
.Get (aFunctionName
, NULL
), NS_ERROR_FAILURE
);
845 int srv
= sqlite3_create_function (mDBConn
,
846 nsPromiseFlatCString(aFunctionName
).get(),
853 if (srv
!= SQLITE_OK
) {
854 HandleSqliteError(nsnull
);
855 return ConvertResultCode(srv
);
858 mFunctions
.Remove (aFunctionName
);
864 mozStorageConnection::s_ProgressHelper(void *arg
)
866 mozStorageConnection
*_this
= static_cast<mozStorageConnection
*>(arg
);
867 return _this
->ProgressHandler();
871 mozStorageConnection::SetProgressHandler(PRInt32 aGranularity
,
872 mozIStorageProgressHandler
*aHandler
,
873 mozIStorageProgressHandler
**aOldHandler
)
875 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
877 // Return previous one
878 nsAutoLock
mutex(mProgressHandlerMutex
);
879 NS_IF_ADDREF(*aOldHandler
= mProgressHandler
);
881 if (!aHandler
|| aGranularity
<= 0) {
885 mProgressHandler
= aHandler
;
886 sqlite3_progress_handler (mDBConn
, aGranularity
, s_ProgressHelper
, this);
892 mozStorageConnection::RemoveProgressHandler(mozIStorageProgressHandler
**aOldHandler
)
894 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
896 // Return previous one
897 nsAutoLock
mutex(mProgressHandlerMutex
);
898 NS_IF_ADDREF(*aOldHandler
= mProgressHandler
);
900 mProgressHandler
= nsnull
;
901 sqlite3_progress_handler (mDBConn
, 0, NULL
, NULL
);
907 mozStorageConnection::ProgressHandler()
909 nsAutoLock
mutex(mProgressHandlerMutex
);
910 if (mProgressHandler
) {
912 nsresult rv
= mProgressHandler
->OnProgress(this, &res
);
913 if (NS_FAILED(rv
)) return 0; // Don't break request
923 mozStorageConnection::HandleSqliteError(const char *aSqlStatement
)
925 // an error just occured!
927 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("Sqlite error: %d '%s'", sqlite3_errcode(mDBConn
), sqlite3_errmsg(mDBConn
)));
929 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("Statement was: %s", aSqlStatement
));