Bug 460546 - nsBrowserGlue should use a smart getter for the pref service. r=gavin
[wine-gecko.git] / storage / src / mozStorageConnection.cpp
blob6e82f9f81faaf613192e4f773f6eb9fe3a36f738
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: sw=4 ts=4 sts=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
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>
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 ***** */
43 #include <stdio.h>
45 #include "nsError.h"
46 #include "nsIMutableArray.h"
47 #include "nsHashSets.h"
48 #include "nsAutoPtr.h"
49 #include "nsIFile.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"
65 #include "prlog.h"
66 #include "prprf.h"
68 #ifdef PR_LOGGING
69 PRLogModuleInfo* gStorageLog = nsnull;
70 #endif
72 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
74 NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
76 mozStorageConnection::mozStorageConnection(mozIStorageService* aService) :
77 mDBConn(nsnull)
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)
85 mFunctions.Init();
88 mozStorageConnection::~mozStorageConnection()
90 (void)Close();
91 nsAutoLock::DestroyLock(mTransactionMutex);
92 nsAutoLock::DestroyLock(mFunctionsMutex);
93 nsAutoLock::DestroyLock(mProgressHandlerMutex);
96 #ifdef PR_LOGGING
97 void tracefunc (void *closure, const char *stmt)
99 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", closure,
100 stmt));
102 #endif
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.
108 NS_IMETHODIMP
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);
116 int srv;
117 nsresult rv;
119 mDatabaseFile = aDatabaseFile;
121 if (aDatabaseFile) {
122 nsAutoString path;
123 rv = aDatabaseFile->GetPath(path);
124 NS_ENSURE_SUCCESS(rv, rv);
126 srv = sqlite3_open (NS_ConvertUTF16toUTF8(path).get(), &mDBConn);
127 } else {
128 // in memory database requested, sqlite uses a magic file name
129 srv = sqlite3_open (":memory:", &mDBConn);
131 if (srv != SQLITE_OK) {
132 mDBConn = nsnull;
133 return ConvertResultCode(srv);
136 #ifdef PR_LOGGING
137 if (! gStorageLog)
138 gStorageLog = PR_NewLogModule("mozStorage");
140 sqlite3_trace (mDBConn, tracefunc, this);
142 nsCAutoString leafName(":memory");
143 if (aDatabaseFile)
144 (void)aDatabaseFile->GetNativeLeafName(leafName);
145 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
146 leafName.get(), this));
147 #endif
149 // Hook up i18n functions
150 srv = StorageUnicodeFunctions::RegisterFunctions(mDBConn);
151 if (srv != SQLITE_OK) {
152 mDBConn = nsnull;
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,
161 NULL);
163 if (srv == SQLITE_OK) {
164 srv = sqlite3_step(stmt);
166 if (srv == SQLITE_DONE || srv == SQLITE_ROW)
167 srv = SQLITE_OK;
168 } else {
169 stmt = nsnull;
172 if (stmt != nsnull)
173 sqlite3_finalize (stmt);
175 if (srv != SQLITE_OK) {
176 sqlite3_close (mDBConn);
177 mDBConn = nsnull;
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
185 if (pref)
186 (void)pref->GetIntPref(PREF_TS_SYNCHRONOUS, &synchronous);
188 switch (synchronous) {
189 case 2:
190 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
191 "PRAGMA synchronous = FULL;"));
192 break;
193 case 0:
194 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
195 "PRAGMA synchronous = OFF;"));
196 break;
197 case 1:
198 default:
199 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
200 "PRAGMA synchronous = NORMAL;"));
201 break;
204 return NS_OK;
207 /*****************************************************************************
208 ** mozIStorageConnection interface
209 *****************************************************************************/
212 ** Core status/initialization
215 NS_IMETHODIMP
216 mozStorageConnection::Close()
218 if (!mDBConn)
219 return NS_ERROR_NOT_INITIALIZED;
221 #ifdef PR_LOGGING
222 nsCAutoString leafName(":memory");
223 if (mDatabaseFile)
224 (void)mDatabaseFile->GetNativeLeafName(leafName);
225 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s'",
226 leafName.get()));
227 #endif
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!");
239 mDBConn = NULL;
240 return ConvertResultCode(srv);
243 NS_IMETHODIMP
244 mozStorageConnection::GetConnectionReady(PRBool *aConnectionReady)
246 *aConnectionReady = (mDBConn != nsnull);
247 return NS_OK;
250 NS_IMETHODIMP
251 mozStorageConnection::GetDatabaseFile(nsIFile **aFile)
253 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
255 NS_IF_ADDREF(*aFile = mDatabaseFile);
257 return NS_OK;
260 NS_IMETHODIMP
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;
268 return NS_OK;
271 NS_IMETHODIMP
272 mozStorageConnection::GetLastError(PRInt32 *aLastError)
274 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
276 *aLastError = sqlite3_errcode(mDBConn);
278 return NS_OK;
281 NS_IMETHODIMP
282 mozStorageConnection::GetLastErrorString(nsACString& aLastErrorString)
284 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
286 const char *serr = sqlite3_errmsg(mDBConn);
287 aLastErrorString.Assign(serr);
289 return NS_OK;
292 NS_IMETHODIMP
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;
302 *version = 0;
303 PRBool hasResult;
304 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
305 *version = stmt->AsInt32(0);
307 return NS_OK;
310 NS_IMETHODIMP
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
325 NS_IMETHODIMP
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();
333 if (!statement)
334 return NS_ERROR_OUT_OF_MEMORY;
335 NS_ADDREF(statement);
337 nsresult rv = statement->Initialize (this, aSQLStatement);
338 if (NS_FAILED(rv)) {
339 NS_RELEASE(statement);
340 return rv;
343 *_retval = statement;
344 return NS_OK;
347 NS_IMETHODIMP
348 mozStorageConnection::ExecuteSimpleSQL(const nsACString& aSQLStatement)
350 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
352 int srv = sqlite3_exec (mDBConn, PromiseFlatCString(aSQLStatement).get(),
353 NULL, NULL, NULL);
354 if (srv != SQLITE_OK) {
355 HandleSqliteError(nsPromiseFlatCString(aSQLStatement).get());
356 return ConvertResultCode(srv);
359 return NS_OK;
362 nsresult
363 mozStorageConnection::ExecuteAsync(mozIStorageStatement ** aStatements,
364 PRUint32 aNumStatements,
365 mozIStorageStatementCallback *aCallback,
366 mozIStoragePendingStatement **_stmt)
368 int rc = SQLITE_OK;
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,
380 NULL);
381 if (rc != SQLITE_OK)
382 break;
384 // Transfer the bindings
385 rc = sqlite3_transfer_bindings(old_stmt, new_stmt);
386 if (rc != SQLITE_OK)
387 break;
389 if (!stmts.AppendElement(new_stmt)) {
390 rc = SQLITE_NOMEM;
391 break;
395 // Dispatch to the background
396 nsresult rv = NS_OK;
397 if (rc == SQLITE_OK)
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]);
405 if (rc != SQLITE_OK)
406 rv = ConvertResultCode(rc);
409 // Always reset all the statements
410 for (PRUint32 i = 0; i < aNumStatements; i++)
411 (void)aStatements[i]->Reset();
413 return rv;
416 NS_IMETHODIMP
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) {
439 exists = PR_TRUE;
440 } else if (srv == SQLITE_DONE) {
441 exists = PR_FALSE;
442 } else {
443 HandleSqliteError("TableExists finalize");
444 return ConvertResultCode(srv);
447 *_retval = exists;
448 return NS_OK;
451 NS_IMETHODIMP
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);
467 *_retval = PR_FALSE;
469 srv = sqlite3_step(stmt);
470 (void)sqlite3_finalize(stmt);
472 if (srv == SQLITE_ROW) {
473 *_retval = PR_TRUE;
476 return ConvertResultCode(srv);
481 ** Transactions
484 NS_IMETHODIMP
485 mozStorageConnection::GetTransactionInProgress(PRBool *_retval)
487 nsAutoLock mutex(mTransactionMutex);
488 *_retval = mTransactionInProgress;
489 return NS_OK;
492 // XXX do we want to just store compiled statements for these?
493 NS_IMETHODIMP
494 mozStorageConnection::BeginTransaction()
496 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
499 NS_IMETHODIMP
500 mozStorageConnection::BeginTransactionAs(PRInt32 aTransactionType)
502 nsAutoLock mutex(mTransactionMutex);
503 if (mTransactionInProgress)
504 return NS_ERROR_FAILURE;
505 nsresult rv;
506 switch(aTransactionType) {
507 case TRANSACTION_DEFERRED:
508 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
509 break;
510 case TRANSACTION_IMMEDIATE:
511 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
512 break;
513 case TRANSACTION_EXCLUSIVE:
514 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
515 break;
516 default:
517 return NS_ERROR_ILLEGAL_VALUE;
519 if (NS_SUCCEEDED(rv))
520 mTransactionInProgress = PR_TRUE;
521 return NS_OK;
524 NS_IMETHODIMP
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;
533 return rv;
536 NS_IMETHODIMP
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;
544 return rv;
548 ** Table creation
551 NS_IMETHODIMP
552 mozStorageConnection::CreateTable(/*const nsID& aID,*/
553 const char *aTableName,
554 const char *aTableSchema)
556 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
557 int srv;
558 char *buf;
560 buf = PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
561 if (!buf)
562 return NS_ERROR_OUT_OF_MEMORY;
564 srv = sqlite3_exec (mDBConn, buf,
565 NULL, NULL, NULL);
567 PR_smprintf_free(buf);
569 return ConvertResultCode(srv);
573 ** Functions
576 PLDHashOperator
577 mozStorageConnection::s_FindFuncEnum(const nsACString &aKey,
578 nsISupports* aData,
579 void* userArg)
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;
589 PRBool
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);
595 return args.mFound;
598 static nsresult
599 mozStorageVariantToSQLite3Result(sqlite3_context *ctx,
600 nsIVariant *var)
602 nsresult rv;
603 PRUint16 dt;
604 // Allow to return NULL not wrapped to
605 // nsIVariant for speed
606 if (!var) {
607 sqlite3_result_null (ctx);
608 return NS_OK;
610 (void)var->GetDataType( &dt );
611 switch (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:
618 PRInt32 v;
619 rv = var->GetAsInt32 (&v);
620 if (NS_FAILED(rv)) return rv;
621 sqlite3_result_int (ctx, v);
623 break;
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:
629 PRInt64 v;
630 rv = var->GetAsInt64 (&v);
631 if (NS_FAILED(rv)) return rv;
632 sqlite3_result_int64 (ctx, v);
634 break;
635 case nsIDataType::VTYPE_FLOAT:
636 case nsIDataType::VTYPE_DOUBLE:
638 double v;
639 rv = var->GetAsDouble (&v);
640 if (NS_FAILED(rv)) return rv;
641 sqlite3_result_double (ctx, v);
643 break;
644 case nsIDataType::VTYPE_BOOL:
646 PRBool v;
647 rv = var->GetAsBool(&v);
648 if (NS_FAILED(rv)) return rv;
649 sqlite3_result_int (ctx, v ? 1 : 0);
651 break;
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:
663 nsAutoString v;
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(),
671 v.Length() * 2,
672 SQLITE_TRANSIENT);
674 break;
675 case nsIDataType::VTYPE_VOID:
676 case nsIDataType::VTYPE_EMPTY:
677 sqlite3_result_null (ctx);
678 break;
679 // Maybe, it'll be possible to convert these
680 // in future too.
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:
686 default:
687 return NS_ERROR_CANNOT_CONVERT_DATA;
689 return NS_OK;
692 static void
693 mozStorageSqlFuncHelper (sqlite3_context *ctx,
694 int argc,
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);
703 if (!ava)
704 return;
705 nsCOMPtr<nsIVariant> retval;
706 nsresult rv = userFunction->OnFunctionCall (ava, getter_AddRefs(retval));
707 if (NS_FAILED(rv)) {
708 NS_WARNING("mozIStorageConnection: User function returned error code!\n");
709 sqlite3_result_error(ctx,
710 "User function returned error code",
711 -1);
712 return;
714 rv = mozStorageVariantToSQLite3Result(ctx,retval);
715 if (NS_FAILED(rv)) {
716 NS_WARNING("mozIStorageConnection: User function returned invalid data type!\n");
717 sqlite3_result_error(ctx,
718 "User function returned invalid data type",
719 -1);
723 NS_IMETHODIMP
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(),
738 aNumArguments,
739 SQLITE_ANY,
740 aFunction,
741 mozStorageSqlFuncHelper,
742 NULL,
743 NULL);
744 if (srv != SQLITE_OK) {
745 HandleSqliteError(nsnull);
746 return ConvertResultCode(srv);
749 if (mFunctions.Put (aFunctionName, aFunction)) {
750 return NS_OK;
752 return NS_ERROR_OUT_OF_MEMORY;
755 static void
756 mozStorageSqlAggrFuncStepHelper (sqlite3_context *ctx,
757 int argc,
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);
767 if (!ava)
768 return;
769 nsresult rv = userFunction->OnStep(ava);
770 if (NS_FAILED(rv))
771 NS_WARNING("mozIStorageConnection: User aggregate step function returned error code!\n");
774 static void
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));
784 if (NS_FAILED(rv)) {
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",
788 -1);
789 return;
791 rv = mozStorageVariantToSQLite3Result(ctx,retval);
792 if (NS_FAILED(rv)) {
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",
796 -1);
800 NS_IMETHODIMP
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?
808 // Check for name.
809 nsAutoLock mutex(mFunctionsMutex);
810 NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
812 // Aggregate functions are stateful, so we cannot have
813 // aliases for them.
814 // Enumerate all functions and determine if this one is already
815 // implemented
816 NS_ENSURE_FALSE(FindFunctionByInstance (aFunction), NS_ERROR_FAILURE);
818 int srv = sqlite3_create_function (mDBConn,
819 nsPromiseFlatCString(aFunctionName).get(),
820 aNumArguments,
821 SQLITE_ANY,
822 aFunction,
823 NULL,
824 mozStorageSqlAggrFuncStepHelper,
825 mozStorageSqlAggrFuncFinalHelper);
826 if (srv != SQLITE_OK) {
827 HandleSqliteError(nsnull);
828 return ConvertResultCode(srv);
831 if (mFunctions.Put (aFunctionName, aFunction)) {
832 return NS_OK;
834 return NS_ERROR_OUT_OF_MEMORY;
837 NS_IMETHODIMP
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(),
848 SQLITE_ANY,
849 NULL,
850 NULL,
851 NULL,
852 NULL);
853 if (srv != SQLITE_OK) {
854 HandleSqliteError(nsnull);
855 return ConvertResultCode(srv);
858 mFunctions.Remove (aFunctionName);
860 return NS_OK;
864 mozStorageConnection::s_ProgressHelper(void *arg)
866 mozStorageConnection *_this = static_cast<mozStorageConnection *>(arg);
867 return _this->ProgressHandler();
870 NS_IMETHODIMP
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) {
882 aHandler = nsnull;
883 aGranularity = 0;
885 mProgressHandler = aHandler;
886 sqlite3_progress_handler (mDBConn, aGranularity, s_ProgressHelper, this);
888 return NS_OK;
891 NS_IMETHODIMP
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);
903 return NS_OK;
907 mozStorageConnection::ProgressHandler()
909 nsAutoLock mutex(mProgressHandlerMutex);
910 if (mProgressHandler) {
911 PRBool res;
912 nsresult rv = mProgressHandler->OnProgress(this, &res);
913 if (NS_FAILED(rv)) return 0; // Don't break request
914 return res ? 1 : 0;
916 return 0;
920 ** Other bits
922 void
923 mozStorageConnection::HandleSqliteError(const char *aSqlStatement)
925 // an error just occured!
926 #ifdef PR_LOGGING
927 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Sqlite error: %d '%s'", sqlite3_errcode(mDBConn), sqlite3_errmsg(mDBConn)));
928 if (aSqlStatement)
929 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Statement was: %s", aSqlStatement));
930 #endif