Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / storage / src / mozStorageConnection.cpp
blob8f1112f73538503501278066589d1aa24a85677c
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"
53 #include "nsThreadUtils.h"
55 #include "mozIStorageAggregateFunction.h"
56 #include "mozIStorageFunction.h"
58 #include "mozStorageEvents.h"
59 #include "mozStorageUnicodeFunctions.h"
60 #include "mozStorageConnection.h"
61 #include "mozStorageService.h"
62 #include "mozStorageStatement.h"
63 #include "mozStorageValueArray.h"
64 #include "mozStoragePrivateHelpers.h"
66 #include "prlog.h"
67 #include "prprf.h"
69 #ifdef PR_LOGGING
70 PRLogModuleInfo* gStorageLog = nsnull;
71 #endif
73 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
75 NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
77 mozStorageConnection::mozStorageConnection(mozIStorageService* aService) :
78 mDBConn(nsnull)
79 , mAsyncExecutionMutex(nsAutoLock::NewLock("AsyncExecutionMutex"))
80 , mAsyncExecutionThreadShuttingDown(PR_FALSE)
81 , mTransactionMutex(nsAutoLock::NewLock("TransactionMutex"))
82 , mTransactionInProgress(PR_FALSE)
83 , mFunctionsMutex(nsAutoLock::NewLock("FunctionsMutex"))
84 , mProgressHandlerMutex(nsAutoLock::NewLock("ProgressHandlerMutex"))
85 , mProgressHandler(nsnull)
86 , mStorageService(aService)
88 mFunctions.Init();
91 mozStorageConnection::~mozStorageConnection()
93 (void)Close();
94 nsAutoLock::DestroyLock(mAsyncExecutionMutex);
95 nsAutoLock::DestroyLock(mTransactionMutex);
96 nsAutoLock::DestroyLock(mFunctionsMutex);
97 nsAutoLock::DestroyLock(mProgressHandlerMutex);
100 #ifdef PR_LOGGING
101 void tracefunc (void *closure, const char *stmt)
103 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", closure,
104 stmt));
106 #endif
109 * Actually creates the connection from the DB. Called by mozStorageService.
110 * You can pass a NULL database file in to get an sqlite in-memory database.
112 NS_IMETHODIMP
113 mozStorageConnection::Initialize(nsIFile *aDatabaseFile)
115 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
116 NS_ENSURE_TRUE(mAsyncExecutionMutex, NS_ERROR_OUT_OF_MEMORY);
117 NS_ENSURE_TRUE(mTransactionMutex, NS_ERROR_OUT_OF_MEMORY);
118 NS_ENSURE_TRUE(mFunctionsMutex, NS_ERROR_OUT_OF_MEMORY);
119 NS_ENSURE_TRUE(mProgressHandlerMutex, NS_ERROR_OUT_OF_MEMORY);
121 int srv;
122 nsresult rv;
124 mDatabaseFile = aDatabaseFile;
126 if (aDatabaseFile) {
127 nsAutoString path;
128 rv = aDatabaseFile->GetPath(path);
129 NS_ENSURE_SUCCESS(rv, rv);
131 srv = sqlite3_open (NS_ConvertUTF16toUTF8(path).get(), &mDBConn);
132 } else {
133 // in memory database requested, sqlite uses a magic file name
134 srv = sqlite3_open (":memory:", &mDBConn);
136 if (srv != SQLITE_OK) {
137 mDBConn = nsnull;
138 return ConvertResultCode(srv);
141 #ifdef PR_LOGGING
142 if (! gStorageLog)
143 gStorageLog = PR_NewLogModule("mozStorage");
145 sqlite3_trace (mDBConn, tracefunc, this);
147 nsCAutoString leafName(":memory");
148 if (aDatabaseFile)
149 (void)aDatabaseFile->GetNativeLeafName(leafName);
150 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
151 leafName.get(), this));
152 #endif
154 // Hook up i18n functions
155 srv = StorageUnicodeFunctions::RegisterFunctions(mDBConn);
156 if (srv != SQLITE_OK) {
157 mDBConn = nsnull;
158 return ConvertResultCode(srv);
161 /* Execute a dummy statement to force the db open, and to verify
162 * whether it's valid or not
164 sqlite3_stmt *stmt = nsnull;
165 srv = sqlite3_prepare_v2(mDBConn, "SELECT * FROM sqlite_master", -1, &stmt,
166 NULL);
168 if (srv == SQLITE_OK) {
169 srv = sqlite3_step(stmt);
171 if (srv == SQLITE_DONE || srv == SQLITE_ROW)
172 srv = SQLITE_OK;
173 } else {
174 stmt = nsnull;
177 if (stmt != nsnull)
178 sqlite3_finalize (stmt);
180 if (srv != SQLITE_OK) {
181 sqlite3_close (mDBConn);
182 mDBConn = nsnull;
184 return ConvertResultCode(srv);
187 // Set the synchronous PRAGMA, according to the pref
188 nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
189 PRInt32 synchronous = 1; // Default to NORMAL if pref not set
190 if (pref)
191 (void)pref->GetIntPref(PREF_TS_SYNCHRONOUS, &synchronous);
193 switch (synchronous) {
194 case 2:
195 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
196 "PRAGMA synchronous = FULL;"));
197 break;
198 case 0:
199 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
200 "PRAGMA synchronous = OFF;"));
201 break;
202 case 1:
203 default:
204 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
205 "PRAGMA synchronous = NORMAL;"));
206 break;
209 return NS_OK;
212 /*****************************************************************************
213 ** mozIStorageConnection interface
214 *****************************************************************************/
217 ** Core status/initialization
220 NS_IMETHODIMP
221 mozStorageConnection::Close()
223 if (!mDBConn)
224 return NS_ERROR_NOT_INITIALIZED;
226 #ifdef PR_LOGGING
227 nsCAutoString leafName(":memory");
228 if (mDatabaseFile)
229 (void)mDatabaseFile->GetNativeLeafName(leafName);
230 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s'",
231 leafName.get()));
232 #endif
234 // Flag that we are shutting down the async thread, so that
235 // getAsyncExecutionTarget knows not to expose/create the async thread.
237 nsAutoLock mutex(mAsyncExecutionMutex);
238 mAsyncExecutionThreadShuttingDown = PR_TRUE;
240 // Shutdown the async thread if it exists. (Because we just set the flag,
241 // we are the only code that is going to be touching this variable from here
242 // on out.)
243 if (mAsyncExecutionThread) {
244 mAsyncExecutionThread->Shutdown();
245 mAsyncExecutionThread = nsnull;
248 #ifdef DEBUG
249 // Notify about any non-finalized statements.
250 sqlite3_stmt *stmt = NULL;
251 while (stmt = sqlite3_next_stmt(mDBConn, stmt)) {
252 char *msg = PR_smprintf("SQL statement '%s' was not finalized",
253 sqlite3_sql(stmt));
254 NS_WARNING(msg);
255 PR_smprintf_free(msg);
257 #endif
260 nsAutoLock mutex(mProgressHandlerMutex);
261 if (mProgressHandler)
262 sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
265 int srv = sqlite3_close(mDBConn);
266 if (srv != SQLITE_OK)
267 NS_WARNING("sqlite3_close failed. There are probably outstanding statements that are listed above!");
269 mDBConn = NULL;
270 return ConvertResultCode(srv);
273 NS_IMETHODIMP
274 mozStorageConnection::GetConnectionReady(PRBool *aConnectionReady)
276 *aConnectionReady = (mDBConn != nsnull);
277 return NS_OK;
280 NS_IMETHODIMP
281 mozStorageConnection::GetDatabaseFile(nsIFile **aFile)
283 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
285 NS_IF_ADDREF(*aFile = mDatabaseFile);
287 return NS_OK;
290 NS_IMETHODIMP
291 mozStorageConnection::GetLastInsertRowID(PRInt64 *aLastInsertRowID)
293 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
295 sqlite_int64 id = sqlite3_last_insert_rowid(mDBConn);
296 *aLastInsertRowID = id;
298 return NS_OK;
301 NS_IMETHODIMP
302 mozStorageConnection::GetLastError(PRInt32 *aLastError)
304 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
306 *aLastError = sqlite3_errcode(mDBConn);
308 return NS_OK;
311 NS_IMETHODIMP
312 mozStorageConnection::GetLastErrorString(nsACString& aLastErrorString)
314 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
316 const char *serr = sqlite3_errmsg(mDBConn);
317 aLastErrorString.Assign(serr);
319 return NS_OK;
322 NS_IMETHODIMP
323 mozStorageConnection::GetSchemaVersion(PRInt32 *version)
325 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
327 nsCOMPtr<mozIStorageStatement> stmt;
328 nsresult rv = CreateStatement(NS_LITERAL_CSTRING(
329 "PRAGMA user_version"), getter_AddRefs(stmt));
330 if (NS_FAILED(rv)) return rv;
332 *version = 0;
333 PRBool hasResult;
334 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
335 *version = stmt->AsInt32(0);
337 return NS_OK;
340 NS_IMETHODIMP
341 mozStorageConnection::SetSchemaVersion(PRInt32 aVersion)
343 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
345 nsCAutoString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
346 stmt.AppendInt(aVersion);
348 return ExecuteSimpleSQL(stmt);
352 ** Statements & Queries
355 NS_IMETHODIMP
356 mozStorageConnection::CreateStatement(const nsACString& aSQLStatement,
357 mozIStorageStatement **_retval)
359 NS_ENSURE_ARG_POINTER(_retval);
360 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
362 mozStorageStatement *statement = new mozStorageStatement();
363 if (!statement)
364 return NS_ERROR_OUT_OF_MEMORY;
365 NS_ADDREF(statement);
367 nsresult rv = statement->Initialize (this, aSQLStatement);
368 if (NS_FAILED(rv)) {
369 NS_RELEASE(statement);
370 return rv;
373 *_retval = statement;
374 return NS_OK;
377 NS_IMETHODIMP
378 mozStorageConnection::ExecuteSimpleSQL(const nsACString& aSQLStatement)
380 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
382 int srv = sqlite3_exec (mDBConn, PromiseFlatCString(aSQLStatement).get(),
383 NULL, NULL, NULL);
384 if (srv != SQLITE_OK) {
385 HandleSqliteError(nsPromiseFlatCString(aSQLStatement).get());
386 return ConvertResultCode(srv);
389 return NS_OK;
392 nsresult
393 mozStorageConnection::ExecuteAsync(mozIStorageStatement ** aStatements,
394 PRUint32 aNumStatements,
395 mozIStorageStatementCallback *aCallback,
396 mozIStoragePendingStatement **_stmt)
398 int rc = SQLITE_OK;
399 nsTArray<sqlite3_stmt *> stmts(aNumStatements);
400 for (PRUint32 i = 0; i < aNumStatements && rc == SQLITE_OK; i++) {
401 sqlite3_stmt *old_stmt = aStatements[i]->GetNativeStatementPointer();
402 NS_ASSERTION(sqlite3_db_handle(old_stmt) == mDBConn,
403 "Statement must be from this database connection!");
405 // Clone this statement. We only need a sqlite3_stmt object, so we can
406 // avoid all the extra work that making a new mozStorageStatement would
407 // normally involve and use the SQLite API.
408 sqlite3_stmt *new_stmt;
409 rc = sqlite3_prepare_v2(mDBConn, sqlite3_sql(old_stmt), -1, &new_stmt,
410 NULL);
411 if (rc != SQLITE_OK)
412 break;
414 // Transfer the bindings
415 rc = sqlite3_transfer_bindings(old_stmt, new_stmt);
416 if (rc != SQLITE_OK)
417 break;
419 if (!stmts.AppendElement(new_stmt)) {
420 rc = SQLITE_NOMEM;
421 break;
425 // Dispatch to the background
426 nsresult rv = NS_OK;
427 if (rc == SQLITE_OK)
428 rv = NS_executeAsync(stmts, this, aCallback, _stmt);
430 // We had a failure, so we need to clean up...
431 if (rc != SQLITE_OK || NS_FAILED(rv)) {
432 for (PRUint32 i = 0; i < stmts.Length(); i++)
433 (void)sqlite3_finalize(stmts[i]);
435 if (rc != SQLITE_OK)
436 rv = ConvertResultCode(rc);
439 // Always reset all the statements
440 for (PRUint32 i = 0; i < aNumStatements; i++)
441 (void)aStatements[i]->Reset();
443 return rv;
446 NS_IMETHODIMP
447 mozStorageConnection::TableExists(const nsACString& aSQLStatement, PRBool *_retval)
449 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
451 nsCString query("SELECT name FROM sqlite_master WHERE type = 'table' AND name ='");
452 query.Append(aSQLStatement);
453 query.AppendLiteral("'");
455 sqlite3_stmt *stmt = nsnull;
456 int srv = sqlite3_prepare_v2(mDBConn, query.get(), -1, &stmt, NULL);
457 if (srv != SQLITE_OK) {
458 HandleSqliteError(query.get());
459 return ConvertResultCode(srv);
462 PRBool exists = PR_FALSE;
464 srv = sqlite3_step(stmt);
465 // we just care about the return value from step
466 sqlite3_finalize(stmt);
468 if (srv == SQLITE_ROW) {
469 exists = PR_TRUE;
470 } else if (srv == SQLITE_DONE) {
471 exists = PR_FALSE;
472 } else {
473 HandleSqliteError("TableExists finalize");
474 return ConvertResultCode(srv);
477 *_retval = exists;
478 return NS_OK;
481 NS_IMETHODIMP
482 mozStorageConnection::IndexExists(const nsACString& aIndexName, PRBool* _retval)
484 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
486 nsCString query("SELECT name FROM sqlite_master WHERE type = 'index' AND name ='");
487 query.Append(aIndexName);
488 query.AppendLiteral("'");
490 sqlite3_stmt *stmt = nsnull;
491 int srv = sqlite3_prepare_v2(mDBConn, query.get(), -1, &stmt, NULL);
492 if (srv != SQLITE_OK) {
493 HandleSqliteError(query.get());
494 return ConvertResultCode(srv);
497 *_retval = PR_FALSE;
499 srv = sqlite3_step(stmt);
500 (void)sqlite3_finalize(stmt);
502 if (srv == SQLITE_ROW) {
503 *_retval = PR_TRUE;
506 return ConvertResultCode(srv);
511 ** Transactions
514 NS_IMETHODIMP
515 mozStorageConnection::GetTransactionInProgress(PRBool *_retval)
517 nsAutoLock mutex(mTransactionMutex);
518 *_retval = mTransactionInProgress;
519 return NS_OK;
522 // XXX do we want to just store compiled statements for these?
523 NS_IMETHODIMP
524 mozStorageConnection::BeginTransaction()
526 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
529 NS_IMETHODIMP
530 mozStorageConnection::BeginTransactionAs(PRInt32 aTransactionType)
532 nsAutoLock mutex(mTransactionMutex);
533 if (mTransactionInProgress)
534 return NS_ERROR_FAILURE;
535 nsresult rv;
536 switch(aTransactionType) {
537 case TRANSACTION_DEFERRED:
538 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
539 break;
540 case TRANSACTION_IMMEDIATE:
541 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
542 break;
543 case TRANSACTION_EXCLUSIVE:
544 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
545 break;
546 default:
547 return NS_ERROR_ILLEGAL_VALUE;
549 if (NS_SUCCEEDED(rv))
550 mTransactionInProgress = PR_TRUE;
551 return rv;
554 NS_IMETHODIMP
555 mozStorageConnection::CommitTransaction()
557 nsAutoLock mutex(mTransactionMutex);
558 if (!mTransactionInProgress)
559 return NS_ERROR_FAILURE;
560 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
561 if (NS_SUCCEEDED(rv))
562 mTransactionInProgress = PR_FALSE;
563 return rv;
566 NS_IMETHODIMP
567 mozStorageConnection::RollbackTransaction()
569 nsAutoLock mutex(mTransactionMutex);
570 if (!mTransactionInProgress)
571 return NS_ERROR_FAILURE;
572 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
573 if (NS_SUCCEEDED(rv))
574 mTransactionInProgress = PR_FALSE;
575 return rv;
579 ** Table creation
582 NS_IMETHODIMP
583 mozStorageConnection::CreateTable(/*const nsID& aID,*/
584 const char *aTableName,
585 const char *aTableSchema)
587 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
588 int srv;
589 char *buf;
591 buf = PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
592 if (!buf)
593 return NS_ERROR_OUT_OF_MEMORY;
595 srv = sqlite3_exec (mDBConn, buf,
596 NULL, NULL, NULL);
598 PR_smprintf_free(buf);
600 return ConvertResultCode(srv);
604 ** Functions
607 PLDHashOperator
608 mozStorageConnection::s_FindFuncEnum(const nsACString &aKey,
609 nsISupports* aData,
610 void* userArg)
612 FindFuncEnumArgs *args = static_cast<FindFuncEnumArgs *>(userArg);
613 if ((void*)aData == args->mTarget) {
614 args->mFound = PR_TRUE;
615 return PL_DHASH_STOP;
617 return PL_DHASH_NEXT;
620 PRBool
621 mozStorageConnection::FindFunctionByInstance(nsISupports *aInstance)
623 // The lock should already be held by calling functions
624 FindFuncEnumArgs args = { aInstance, PR_FALSE };
625 mFunctions.EnumerateRead(s_FindFuncEnum, &args);
626 return args.mFound;
629 static nsresult
630 mozStorageVariantToSQLite3Result(sqlite3_context *ctx,
631 nsIVariant *var)
633 nsresult rv;
634 PRUint16 dt;
635 // Allow to return NULL not wrapped to
636 // nsIVariant for speed
637 if (!var) {
638 sqlite3_result_null (ctx);
639 return NS_OK;
641 (void)var->GetDataType( &dt );
642 switch (dt) {
643 case nsIDataType::VTYPE_INT8:
644 case nsIDataType::VTYPE_INT16:
645 case nsIDataType::VTYPE_INT32:
646 case nsIDataType::VTYPE_UINT8:
647 case nsIDataType::VTYPE_UINT16:
649 PRInt32 v;
650 rv = var->GetAsInt32 (&v);
651 if (NS_FAILED(rv)) return rv;
652 sqlite3_result_int (ctx, v);
654 break;
655 case nsIDataType::VTYPE_UINT32: // Try to preserve full range
656 case nsIDataType::VTYPE_INT64:
657 // Data loss possible, but there is no unsigned types in SQLite
658 case nsIDataType::VTYPE_UINT64:
660 PRInt64 v;
661 rv = var->GetAsInt64 (&v);
662 if (NS_FAILED(rv)) return rv;
663 sqlite3_result_int64 (ctx, v);
665 break;
666 case nsIDataType::VTYPE_FLOAT:
667 case nsIDataType::VTYPE_DOUBLE:
669 double v;
670 rv = var->GetAsDouble (&v);
671 if (NS_FAILED(rv)) return rv;
672 sqlite3_result_double (ctx, v);
674 break;
675 case nsIDataType::VTYPE_BOOL:
677 PRBool v;
678 rv = var->GetAsBool(&v);
679 if (NS_FAILED(rv)) return rv;
680 sqlite3_result_int (ctx, v ? 1 : 0);
682 break;
683 case nsIDataType::VTYPE_CHAR:
684 case nsIDataType::VTYPE_WCHAR:
685 case nsIDataType::VTYPE_DOMSTRING:
686 case nsIDataType::VTYPE_CHAR_STR:
687 case nsIDataType::VTYPE_WCHAR_STR:
688 case nsIDataType::VTYPE_STRING_SIZE_IS:
689 case nsIDataType::VTYPE_WSTRING_SIZE_IS:
690 case nsIDataType::VTYPE_UTF8STRING:
691 case nsIDataType::VTYPE_CSTRING:
692 case nsIDataType::VTYPE_ASTRING:
694 nsAutoString v;
695 // GetAsAString does proper conversion to UCS2
696 // from all string-like types. It can be used
697 // universally without problems.
698 rv = var->GetAsAString (v);
699 if (NS_FAILED(rv)) return rv;
700 sqlite3_result_text16 (ctx,
701 nsPromiseFlatString(v).get(),
702 v.Length() * 2,
703 SQLITE_TRANSIENT);
705 break;
706 case nsIDataType::VTYPE_VOID:
707 case nsIDataType::VTYPE_EMPTY:
708 sqlite3_result_null (ctx);
709 break;
710 // Maybe, it'll be possible to convert these
711 // in future too.
712 case nsIDataType::VTYPE_ID:
713 case nsIDataType::VTYPE_INTERFACE:
714 case nsIDataType::VTYPE_INTERFACE_IS:
715 case nsIDataType::VTYPE_ARRAY:
716 case nsIDataType::VTYPE_EMPTY_ARRAY:
717 default:
718 return NS_ERROR_CANNOT_CONVERT_DATA;
720 return NS_OK;
723 static void
724 mozStorageSqlFuncHelper (sqlite3_context *ctx,
725 int argc,
726 sqlite3_value **argv)
728 void *userData = sqlite3_user_data (ctx);
729 // We don't want to QI here, because this will be called a -lot-
730 mozIStorageFunction *userFunction =
731 static_cast<mozIStorageFunction *>(userData);
733 nsRefPtr<mozStorageArgvValueArray> ava = new mozStorageArgvValueArray (argc, argv);
734 if (!ava)
735 return;
736 nsCOMPtr<nsIVariant> retval;
737 nsresult rv = userFunction->OnFunctionCall (ava, getter_AddRefs(retval));
738 if (NS_FAILED(rv)) {
739 NS_WARNING("mozIStorageConnection: User function returned error code!\n");
740 sqlite3_result_error(ctx,
741 "User function returned error code",
742 -1);
743 return;
745 rv = mozStorageVariantToSQLite3Result(ctx,retval);
746 if (NS_FAILED(rv)) {
747 NS_WARNING("mozIStorageConnection: User function returned invalid data type!\n");
748 sqlite3_result_error(ctx,
749 "User function returned invalid data type",
750 -1);
754 NS_IMETHODIMP
755 mozStorageConnection::CreateFunction(const nsACString &aFunctionName,
756 PRInt32 aNumArguments,
757 mozIStorageFunction *aFunction)
759 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
761 // do we already have this function defined?
762 // Check for name only because simple function can
763 // be defined multiple times with different names (aliases).
764 nsAutoLock mutex(mFunctionsMutex);
765 NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
767 int srv = sqlite3_create_function (mDBConn,
768 nsPromiseFlatCString(aFunctionName).get(),
769 aNumArguments,
770 SQLITE_ANY,
771 aFunction,
772 mozStorageSqlFuncHelper,
773 NULL,
774 NULL);
775 if (srv != SQLITE_OK) {
776 HandleSqliteError(nsnull);
777 return ConvertResultCode(srv);
780 if (mFunctions.Put (aFunctionName, aFunction)) {
781 return NS_OK;
783 return NS_ERROR_OUT_OF_MEMORY;
786 static void
787 mozStorageSqlAggrFuncStepHelper (sqlite3_context *ctx,
788 int argc,
789 sqlite3_value **argv)
791 void *userData = sqlite3_user_data (ctx);
792 // We don't want to QI here, because this will be called a -lot-
793 mozIStorageAggregateFunction *userFunction =
794 static_cast<mozIStorageAggregateFunction *>(userData);
796 nsRefPtr<mozStorageArgvValueArray> ava =
797 new mozStorageArgvValueArray (argc, argv);
798 if (!ava)
799 return;
800 nsresult rv = userFunction->OnStep(ava);
801 if (NS_FAILED(rv))
802 NS_WARNING("mozIStorageConnection: User aggregate step function returned error code!\n");
805 static void
806 mozStorageSqlAggrFuncFinalHelper (sqlite3_context *ctx)
808 void *userData = sqlite3_user_data (ctx);
809 // We don't want to QI here, because this will be called a -lot-
810 mozIStorageAggregateFunction *userFunction =
811 static_cast<mozIStorageAggregateFunction *>(userData);
813 nsRefPtr<nsIVariant> retval;
814 nsresult rv = userFunction->OnFinal (getter_AddRefs(retval));
815 if (NS_FAILED(rv)) {
816 NS_WARNING("mozIStorageConnection: User aggregate final function returned error code!\n");
817 sqlite3_result_error(ctx,
818 "User aggregate final function returned error code",
819 -1);
820 return;
822 rv = mozStorageVariantToSQLite3Result(ctx,retval);
823 if (NS_FAILED(rv)) {
824 NS_WARNING("mozIStorageConnection: User aggregate final function returned invalid data type!\n");
825 sqlite3_result_error(ctx,
826 "User aggregate final function returned invalid data type",
827 -1);
831 NS_IMETHODIMP
832 mozStorageConnection::CreateAggregateFunction(const nsACString &aFunctionName,
833 PRInt32 aNumArguments,
834 mozIStorageAggregateFunction *aFunction)
836 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
838 // do we already have this function defined?
839 // Check for name.
840 nsAutoLock mutex(mFunctionsMutex);
841 NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
843 // Aggregate functions are stateful, so we cannot have
844 // aliases for them.
845 // Enumerate all functions and determine if this one is already
846 // implemented
847 NS_ENSURE_FALSE(FindFunctionByInstance (aFunction), NS_ERROR_FAILURE);
849 int srv = sqlite3_create_function (mDBConn,
850 nsPromiseFlatCString(aFunctionName).get(),
851 aNumArguments,
852 SQLITE_ANY,
853 aFunction,
854 NULL,
855 mozStorageSqlAggrFuncStepHelper,
856 mozStorageSqlAggrFuncFinalHelper);
857 if (srv != SQLITE_OK) {
858 HandleSqliteError(nsnull);
859 return ConvertResultCode(srv);
862 if (mFunctions.Put (aFunctionName, aFunction)) {
863 return NS_OK;
865 return NS_ERROR_OUT_OF_MEMORY;
868 NS_IMETHODIMP
869 mozStorageConnection::RemoveFunction(const nsACString &aFunctionName)
871 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
873 nsAutoLock mutex(mFunctionsMutex);
874 NS_ENSURE_TRUE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
876 int srv = sqlite3_create_function (mDBConn,
877 nsPromiseFlatCString(aFunctionName).get(),
879 SQLITE_ANY,
880 NULL,
881 NULL,
882 NULL,
883 NULL);
884 if (srv != SQLITE_OK) {
885 HandleSqliteError(nsnull);
886 return ConvertResultCode(srv);
889 mFunctions.Remove (aFunctionName);
891 return NS_OK;
895 mozStorageConnection::s_ProgressHelper(void *arg)
897 mozStorageConnection *_this = static_cast<mozStorageConnection *>(arg);
898 return _this->ProgressHandler();
901 NS_IMETHODIMP
902 mozStorageConnection::SetProgressHandler(PRInt32 aGranularity,
903 mozIStorageProgressHandler *aHandler,
904 mozIStorageProgressHandler **aOldHandler)
906 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
908 // Return previous one
909 nsAutoLock mutex(mProgressHandlerMutex);
910 NS_IF_ADDREF(*aOldHandler = mProgressHandler);
912 if (!aHandler || aGranularity <= 0) {
913 aHandler = nsnull;
914 aGranularity = 0;
916 mProgressHandler = aHandler;
917 sqlite3_progress_handler (mDBConn, aGranularity, s_ProgressHelper, this);
919 return NS_OK;
922 NS_IMETHODIMP
923 mozStorageConnection::RemoveProgressHandler(mozIStorageProgressHandler **aOldHandler)
925 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
927 // Return previous one
928 nsAutoLock mutex(mProgressHandlerMutex);
929 NS_IF_ADDREF(*aOldHandler = mProgressHandler);
931 mProgressHandler = nsnull;
932 sqlite3_progress_handler (mDBConn, 0, NULL, NULL);
934 return NS_OK;
938 mozStorageConnection::ProgressHandler()
940 nsAutoLock mutex(mProgressHandlerMutex);
941 if (mProgressHandler) {
942 PRBool res;
943 nsresult rv = mProgressHandler->OnProgress(this, &res);
944 if (NS_FAILED(rv)) return 0; // Don't break request
945 return res ? 1 : 0;
947 return 0;
951 ** Other bits
954 already_AddRefed<nsIEventTarget>
955 mozStorageConnection::getAsyncExecutionTarget()
957 nsAutoLock mutex(mAsyncExecutionMutex);
959 // If we are shutting down the asynchronous thread, don't hand out any more
960 // references to the thread.
961 if (mAsyncExecutionThreadShuttingDown)
962 return nsnull;
964 if (!mAsyncExecutionThread) {
965 nsresult rv = NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
966 if (NS_FAILED(rv)) {
967 NS_WARNING("Failed to create async thread.");
968 return nsnull;
972 nsIEventTarget *eventTarget;
973 NS_ADDREF(eventTarget = mAsyncExecutionThread);
974 return eventTarget;
977 void
978 mozStorageConnection::HandleSqliteError(const char *aSqlStatement)
980 // an error just occured!
981 #ifdef PR_LOGGING
982 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Sqlite error: %d '%s'", sqlite3_errcode(mDBConn), sqlite3_errmsg(mDBConn)));
983 if (aSqlStatement)
984 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Statement was: %s", aSqlStatement));
985 #endif