--- a/storage/public/Makefile.in
+++ b/storage/public/Makefile.in
@@ -16,16 +16,17 @@
#
# The Initial Developer of the Original Code is
# Oracle Corporation
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
+# Lev Serebryakov <lev@serebryakov.spb.ru>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
@@ -45,17 +46,19 @@ include $(DEPTH)/config/autoconf.mk
MODULE = storage
XPIDL_MODULE = storage
GRE_MODULE = 1
XPIDLSRCS = \
mozIStorageService.idl \
mozIStorageConnection.idl \
+ mozIStorageAggregateFunction.idl \
mozIStorageFunction.idl \
+ mozIStorageProgressHandler.idl \
mozIStorageStatement.idl \
mozIStorageStatementWrapper.idl \
mozIStorageDataSet.idl \
mozIStorageValueArray.idl \
$(NULL)
EXPORTS = \
mozStorageHelper.h \
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageAggregateFunction.idl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface mozIStorageConnection;
+interface mozIStorageValueArray;
+interface nsIArray;
+interface nsIVariant;
+
+/**
+ * mozIStorageAggregateFunction represents aggregate SQL function.
+ * Common examples of aggregate functions are SUM() and COUNT().
+ *
+ * An aggregate function calculates one result for a given set of data, where
+ * a set of data is a group of tuples. There can be one group
+ * per request or many of them, if GROUP BY clause is used or not.
+ */
+[scriptable, uuid(763217b7-3123-11da-918d-000347412e16)]
+interface mozIStorageAggregateFunction : nsISupports {
+ /**
+ * onStep is called when next value should be passed to
+ * a custom function.
+ *
+ * @param aFunctionArguments The arguments passed in to the function
+ */
+ void onStep(in mozIStorageValueArray aFunctionArguments);
+
+ /**
+ * Called when all tuples in a group have been processed and the engine
+ * needs the aggregate function's value.
+ *
+ * @returns aggregate result as Variant.
+ */
+ nsIVariant onFinal();
+};
--- a/storage/public/mozIStorageConnection.idl
+++ b/storage/public/mozIStorageConnection.idl
@@ -18,16 +18,17 @@
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
* Brett Wilson <brettw@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -35,28 +36,30 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
+interface mozIStorageAggregateFunction;
interface mozIStorageFunction;
+interface mozIStorageProgressHandler;
interface mozIStorageStatement;
interface nsIFile;
/**
* mozIStorageConnection represents a database connection attached to
* a specific file or to the in-memory data storage. It is the
* primary interface for interacting with a database, including
* creating prepared statements, executing SQL, and examining database
* errors.
*/
-[scriptable, uuid(43933f76-3376-4999-9096-6f1d545fe5f6)]
+[scriptable, uuid(9f36de9d-6471-4249-afed-1ee7760e325c)]
interface mozIStorageConnection : nsISupports {
/*
* Initialization and status
*/
/**
* whether the database is open or not
*/
@@ -191,21 +194,67 @@ interface mozIStorageConnection : nsISup
in string aTableSchema);
/*
* Functions
*/
/**
* Create a new SQLite function
+ *
+ * @param aFunctionName The name of function to create, as seen in SQL.
+ * @param aNumArguments The number of arguments the function takes. Pass
+ * -1 for variable-argument functions.
+ * @param aFunction The instance of mozIStorageFunction, which implements
+ * the function in question.
*/
- void createFunction(in string aFunctionName,
+ void createFunction(in AUTF8String aFunctionName,
in long aNumArguments,
in mozIStorageFunction aFunction);
+ /**
+ * Create a new SQLite aggregate function
+ *
+ * @param aFunctionName The name of aggregate function to create, as seen
+ * in SQL.
+ * @param aNumArguments The number of arguments the function takes. Pass
+ * -1 for variable-argument functions.
+ * @param aFunction The instance of mozIStorageAggreagteFunction,
+ * which implements the function in question.
+ */
+ void createAggregateFunction(in AUTF8String aFunctionName,
+ in long aNumArguments,
+ in mozIStorageAggregateFunction aFunction);
+ /**
+ * Delete custom SQLite function (simple or aggregate one)
+ *
+ * @param aFunctionName The name of function to remove.
+ */
+ void removeFunction(in AUTF8String aFunctionName);
+
+ /**
+ * Sets a progress handler. Only one handler can be registered at a time.
+ * If you need more than one, you need to chain them yourself.
+ *
+ * @param aGranularity The number of SQL virtual machine steps between
+ * progress handler callbacks.
+ * @param aHandler The instance of mozIStorageProgressHandler.
+ *
+ * @return previous registered handler.
+ */
+ mozIStorageProgressHandler setProgressHandler(in PRInt32 aGranularity,
+ in mozIStorageProgressHandler aHandler);
+
+ /**
+ * Remove a progress handler.
+ *
+ * @return previous registered handler.
+ */
+ mozIStorageProgressHandler removeProgressHandler();
+
/*
* Utilities
*/
/**
* Copies the current database file to the specified parent directory with the
* specified file name. If the parent directory is not specified, it places
* the backup in the same directory as the current file. This function
--- a/storage/public/mozIStorageFunction.idl
+++ b/storage/public/mozIStorageFunction.idl
@@ -16,16 +16,17 @@
*
* The Initial Developer of the Original Code is
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -36,26 +37,37 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface mozIStorageConnection;
interface mozIStorageValueArray;
interface nsIArray;
+interface nsIVariant;
/**
* mozIStorageFunction is to be implemented by storage consumers that
- * wish to define custom storage functions, through
- * mozIStorageConnection's createFunction method.
+ * wish to receive callbacks during the request execution.
+ *
+ * SQL can apply functions to values from tables. Examples of
+ * such functions are MIN(a1,a2) or SQRT(num). Many functions are
+ * implemented in SQL engine.
+ *
+ * This interface allows consumers to implement their own,
+ * problem-specific functions.
+ * These functions can be called from triggers, too.
+ *
*/
-[scriptable, uuid(898d4189-7012-4ae9-a2af-435491cfa114)]
+[scriptable, uuid(9ff02465-21cb-49f3-b975-7d5b38ceec73)]
interface mozIStorageFunction : nsISupports {
/**
* onFunctionCall is called when execution of a custom
- * function should occur. There are no return values.
+ * function should occur.
*
* @param aNumArguments The number of arguments
* @param aFunctionArguments The arguments passed in to the function
+ *
+ * @returns any value as Variant type.
*/
- void onFunctionCall(in mozIStorageValueArray aFunctionArguments);
+ nsIVariant onFunctionCall(in mozIStorageValueArray aFunctionArguments);
};
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageProgressHandler.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface mozIStorageConnection;
+
+/**
+ * mozIProgressHandler is to be implemented by storage consumers that
+ * wish to receive callbacks during the request execution.
+ */
+[scriptable, uuid(a3a6fcd4-bf89-4208-a837-bf2a73afd30c)]
+interface mozIStorageProgressHandler : nsISupports {
+ /**
+ * onProgress is invoked periodically during long running calls.
+ *
+ * @param aConnection connection, for which progress handler is
+ * invoked.
+ *
+ * @return true to abort request, false to continue work.
+ */
+
+ boolean onProgress(in mozIStorageConnection aConnection);
+};
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -19,16 +19,17 @@
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
* Brett Wilson <brettw@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -38,18 +39,22 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include "nsError.h"
#include "nsIMutableArray.h"
+#include "nsHashSets.h"
+#include "nsAutoPtr.h"
#include "nsIFile.h"
+#include "nsIVariant.h"
+#include "mozIStorageAggregateFunction.h"
#include "mozIStorageFunction.h"
#include "mozStorageConnection.h"
#include "mozStorageService.h"
#include "mozStorageStatement.h"
#include "mozStorageValueArray.h"
#include "mozStorage.h"
@@ -59,31 +64,36 @@
#ifdef PR_LOGGING
PRLogModuleInfo* gStorageLog = nsnull;
#endif
NS_IMPL_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
mozStorageConnection::mozStorageConnection(mozIStorageService* aService)
: mDBConn(nsnull), mTransactionInProgress(PR_FALSE),
+ mProgressHandler(nsnull),
mStorageService(aService)
{
-
+ mFunctions.Init();
}
mozStorageConnection::~mozStorageConnection()
{
if (mDBConn) {
+ if (mProgressHandler)
+ sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
int srv = sqlite3_close (mDBConn);
- if (srv != SQLITE_OK) {
- NS_WARNING("sqlite3_close failed. There are probably outstanding statements!");
- }
+ if (srv != SQLITE_OK)
+ NS_WARNING("sqlite3_close failed. There are probably outstanding statements!");
// make sure it really got closed
((mozStorageService*)(mStorageService.get()))->FlushAsyncIO();
+
+ // Release all functions
+ mFunctions.EnumerateRead(s_ReleaseFuncEnum, NULL);
}
}
#ifdef PR_LOGGING
void tracefunc (void *closure, const char *stmt)
{
PR_LOG(gStorageLog, PR_LOG_DEBUG, ("%s", stmt));
}
@@ -126,42 +136,39 @@ mozStorageConnection::Initialize(nsIFile
#endif
/* Execute a dummy statement to force the db open, and to verify
* whether it's valid or not
*/
sqlite3_stmt *stmt = nsnull;
nsCString query("SELECT * FROM sqlite_master");
srv = sqlite3_prepare (mDBConn, query.get(), query.Length(), &stmt, nsnull);
-
+
if (srv == SQLITE_OK) {
srv = sqlite3_step(stmt);
-
+
if (srv == SQLITE_DONE || srv == SQLITE_ROW)
srv = SQLITE_OK;
} else {
stmt = nsnull;
}
if (stmt != nsnull)
sqlite3_finalize (stmt);
-
+
if (srv != SQLITE_OK) {
sqlite3_close (mDBConn);
mDBConn = nsnull;
// make sure it really got closed
((mozStorageService*)(mStorageService.get()))->FlushAsyncIO();
return ConvertResultCode(srv);
}
- mFunctions = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
- if (NS_FAILED(rv)) return rv;
-
return NS_OK;
}
/*****************************************************************************
** mozIStorageConnection interface
*****************************************************************************/
/**
@@ -173,59 +180,59 @@ mozStorageConnection::GetConnectionReady
{
*aConnectionReady = (mDBConn != nsnull);
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::GetDatabaseFile(nsIFile **aFile)
{
- NS_ASSERTION(mDBConn, "connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
NS_IF_ADDREF(*aFile = mDatabaseFile);
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::GetLastInsertRowID(PRInt64 *aLastInsertRowID)
{
- NS_ASSERTION(mDBConn, "connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
sqlite_int64 id = sqlite3_last_insert_rowid(mDBConn);
*aLastInsertRowID = id;
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::GetLastError(PRInt32 *aLastError)
{
- NS_ASSERTION(mDBConn, "connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
*aLastError = sqlite3_errcode(mDBConn);
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::GetLastErrorString(nsACString& aLastErrorString)
{
- NS_ASSERTION(mDBConn, "connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
const char *serr = sqlite3_errmsg(mDBConn);
aLastErrorString.Assign(serr);
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::GetSchemaVersion(PRInt32 *version)
{
- NS_ASSERTION(mDBConn, "Connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA user_version"), getter_AddRefs(stmt));
if (NS_FAILED(rv)) return rv;
*version = 0;
PRBool hasResult;
@@ -233,34 +240,34 @@ mozStorageConnection::GetSchemaVersion(P
*version = stmt->AsInt32(0);
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::SetSchemaVersion(PRInt32 aVersion)
{
- NS_ASSERTION(mDBConn, "Connection not initialized");
-
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
nsCAutoString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
stmt.AppendInt(aVersion);
return ExecuteSimpleSQL(stmt);
}
/**
** Statements & Queries
**/
NS_IMETHODIMP
mozStorageConnection::CreateStatement(const nsACString& aSQLStatement,
mozIStorageStatement **_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
- NS_ASSERTION(mDBConn, "connection not initialized");
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
mozStorageStatement *statement = new mozStorageStatement();
if (!statement)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(statement);
nsresult rv = statement->Initialize (this, aSQLStatement);
if (NS_FAILED(rv)) {
@@ -270,32 +277,32 @@ mozStorageConnection::CreateStatement(co
*_retval = statement;
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::ExecuteSimpleSQL(const nsACString& aSQLStatement)
{
- NS_ENSURE_ARG_POINTER(mDBConn);
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
int srv = sqlite3_exec (mDBConn, PromiseFlatCString(aSQLStatement).get(),
NULL, NULL, NULL);
if (srv != SQLITE_OK) {
HandleSqliteError(nsPromiseFlatCString(aSQLStatement).get());
return ConvertResultCode(srv);
}
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::TableExists(const nsACString& aSQLStatement, PRBool *_retval)
{
- NS_ENSURE_ARG_POINTER(mDBConn);
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
nsCString query("SELECT name FROM sqlite_master WHERE type = 'table' AND name ='");
query.Append(aSQLStatement);
query.AppendLiteral("'");
sqlite3_stmt *stmt = nsnull;
int srv = sqlite3_prepare (mDBConn, query.get(), query.Length(), &stmt, nsnull);
if (srv != SQLITE_OK) {
@@ -320,17 +327,17 @@ mozStorageConnection::TableExists(const
*_retval = exists;
return NS_OK;
}
NS_IMETHODIMP
mozStorageConnection::IndexExists(const nsACString& aIndexName, PRBool* _retval)
{
- NS_ENSURE_ARG_POINTER(mDBConn);
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
nsCString query("SELECT name FROM sqlite_master WHERE type = 'index' AND name ='");
query.Append(aIndexName);
query.AppendLiteral("'");
sqlite3_stmt *stmt = nsnull;
int srv = sqlite3_prepare(mDBConn, query.get(), query.Length(), &stmt, nsnull);
if (srv != SQLITE_OK) {
@@ -446,60 +453,368 @@ mozStorageConnection::CreateTable(/*cons
return ConvertResultCode(srv);
}
/**
** Functions
**/
+PLDHashOperator
+mozStorageConnection::s_FindFuncEnum(const nsACString &aKey,
+ nsISupports* aData,
+ void* userArg)
+{
+ FindFuncEnumArgs *args = static_cast<FindFuncEnumArgs *>(userArg);
+ if ((void*)aData == args->mTarget) {
+ args->mFound = PR_TRUE;
+ return PL_DHASH_STOP;
+ }
+ return PL_DHASH_NEXT;
+}
+
+PLDHashOperator
+mozStorageConnection::s_ReleaseFuncEnum(const nsACString &aKey,
+ nsISupports* aData,
+ void* userArg)
+{
+ NS_RELEASE(aData);
+ return PL_DHASH_NEXT;
+}
+
+PRBool
+mozStorageConnection::FindFunctionByInstance(nsISupports *aInstance)
+{
+ FindFuncEnumArgs args = { aInstance, PR_FALSE };
+ mFunctions.EnumerateRead(s_FindFuncEnum, &args);
+ return args.mFound;
+}
+
+static nsresult
+mozStorageVariantToSQLite3Result(sqlite3_context *ctx,
+ nsIVariant *var)
+{
+ nsresult rv;
+ PRUint16 dt;
+ // Allow to return NULL not wrapped to
+ // nsIVariant for speed
+ if (!var) {
+ sqlite3_result_null (ctx);
+ return NS_OK;
+ }
+ (void)var->GetDataType( &dt );
+ switch (dt) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ {
+ PRInt32 v;
+ rv = var->GetAsInt32 (&v);
+ if (NS_FAILED(rv)) return rv;
+ sqlite3_result_int (ctx, v);
+ }
+ break;
+ case nsIDataType::VTYPE_UINT32: // Try to preserve full range
+ case nsIDataType::VTYPE_INT64:
+ // Data loss possible, but there is no unsigned types in SQLite
+ case nsIDataType::VTYPE_UINT64:
+ {
+ PRInt64 v;
+ rv = var->GetAsInt64 (&v);
+ if (NS_FAILED(rv)) return rv;
+ sqlite3_result_int64 (ctx, v);
+ }
+ break;
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ {
+ double v;
+ rv = var->GetAsDouble (&v);
+ if (NS_FAILED(rv)) return rv;
+ sqlite3_result_double (ctx, v);
+ }
+ break;
+ case nsIDataType::VTYPE_BOOL:
+ {
+ PRBool v;
+ rv = var->GetAsBool(&v);
+ if (NS_FAILED(rv)) return rv;
+ sqlite3_result_int (ctx, v ? 1 : 0);
+ }
+ break;
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_ASTRING:
+ {
+ nsAutoString v;
+ // GetAsAString does proper conversion to UCS2
+ // from all string-like types. It can be used
+ // universally without problems.
+ rv = var->GetAsAString (v);
+ if (NS_FAILED(rv)) return rv;
+ sqlite3_result_text16 (ctx,
+ nsPromiseFlatString(v).get(),
+ v.Length() * 2,
+ SQLITE_TRANSIENT);
+ }
+ break;
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_EMPTY:
+ sqlite3_result_null (ctx);
+ break;
+ // Maybe, it'll be possible to convert these
+ // in future too.
+ case nsIDataType::VTYPE_ID:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+ return NS_OK;
+}
+
static void
mozStorageSqlFuncHelper (sqlite3_context *ctx,
int argc,
sqlite3_value **argv)
{
void *userData = sqlite3_user_data (ctx);
// We don't want to QI here, because this will be called a -lot-
- mozIStorageFunction *userFunction = NS_STATIC_CAST(mozIStorageFunction *, userData);
+ mozIStorageFunction *userFunction =
+ static_cast<mozIStorageFunction *>(userData);
- nsCOMPtr<mozStorageArgvValueArray> ava = new mozStorageArgvValueArray (argc, argv);
- nsresult rv = userFunction->OnFunctionCall (ava);
+ nsRefPtr<mozStorageArgvValueArray> ava = new mozStorageArgvValueArray (argc, argv);
+ if (!ava)
+ return;
+ nsCOMPtr<nsIVariant> retval;
+ nsresult rv = userFunction->OnFunctionCall (ava, getter_AddRefs(retval));
if (NS_FAILED(rv)) {
NS_WARNING("mozIStorageConnection: User function returned error code!\n");
+ sqlite3_result_error(ctx,
+ "User function returned error code",
+ -1);
+ return;
+ }
+ rv = mozStorageVariantToSQLite3Result(ctx,retval);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("mozIStorageConnection: User function returned invalid data type!\n");
+ sqlite3_result_error(ctx,
+ "User function returned invalid data type",
+ -1);
}
}
NS_IMETHODIMP
-mozStorageConnection::CreateFunction(const char *aFunctionName,
+mozStorageConnection::CreateFunction(const nsACString &aFunctionName,
PRInt32 aNumArguments,
mozIStorageFunction *aFunction)
{
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
// do we already have this function defined?
- // XXX check for name as well
- PRUint32 idx;
- nsresult rv = mFunctions->IndexOf (0, aFunction, &idx);
- if (rv != NS_ERROR_FAILURE) {
- // already exists
- return NS_ERROR_FAILURE;
- }
+ // Check for name only because simple function can
+ // be defined multiple times with different names (aliases).
+ NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
int srv = sqlite3_create_function (mDBConn,
- aFunctionName,
+ nsPromiseFlatCString(aFunctionName).get(),
aNumArguments,
SQLITE_ANY,
aFunction,
mozStorageSqlFuncHelper,
- nsnull,
- nsnull);
+ NULL,
+ NULL);
if (srv != SQLITE_OK) {
HandleSqliteError(nsnull);
return ConvertResultCode(srv);
}
- return mFunctions->AppendElement(aFunction, PR_FALSE);
+ if (mFunctions.Put (aFunctionName, aFunction)) {
+ // We hold function object -- add ref to it
+ NS_ADDREF(aFunction);
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+static void
+mozStorageSqlAggrFuncStepHelper (sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv)
+{
+ void *userData = sqlite3_user_data (ctx);
+ // We don't want to QI here, because this will be called a -lot-
+ mozIStorageAggregateFunction *userFunction =
+ static_cast<mozIStorageAggregateFunction *>(userData);
+
+ nsRefPtr<mozStorageArgvValueArray> ava =
+ new mozStorageArgvValueArray (argc, argv);
+ if (!ava)
+ return;
+ nsresult rv = userFunction->OnStep(ava);
+ if (NS_FAILED(rv))
+ NS_WARNING("mozIStorageConnection: User aggregate step function returned error code!\n");
+}
+
+static void
+mozStorageSqlAggrFuncFinalHelper (sqlite3_context *ctx)
+{
+ void *userData = sqlite3_user_data (ctx);
+ // We don't want to QI here, because this will be called a -lot-
+ mozIStorageAggregateFunction *userFunction =
+ static_cast<mozIStorageAggregateFunction *>(userData);
+
+ nsRefPtr<nsIVariant> retval;
+ nsresult rv = userFunction->OnFinal (getter_AddRefs(retval));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("mozIStorageConnection: User aggregate final function returned error code!\n");
+ sqlite3_result_error(ctx,
+ "User aggregate final function returned error code",
+ -1);
+ return;
+ }
+ rv = mozStorageVariantToSQLite3Result(ctx,retval);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("mozIStorageConnection: User aggregate final function returned invalid data type!\n");
+ sqlite3_result_error(ctx,
+ "User aggregate final function returned invalid data type",
+ -1);
+ }
+}
+
+NS_IMETHODIMP
+mozStorageConnection::CreateAggregateFunction(const nsACString &aFunctionName,
+ PRInt32 aNumArguments,
+ mozIStorageAggregateFunction *aFunction)
+{
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
+ // do we already have this function defined?
+ // Check for name.
+ NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
+
+ // Aggregate functions are stateful, so we cannot have
+ // aliases for them.
+ // Enumerate all functions and determine if this one is already
+ // implemented
+ NS_ENSURE_FALSE(FindFunctionByInstance (aFunction), NS_ERROR_FAILURE);
+
+ int srv = sqlite3_create_function (mDBConn,
+ nsPromiseFlatCString(aFunctionName).get(),
+ aNumArguments,
+ SQLITE_ANY,
+ aFunction,
+ NULL,
+ mozStorageSqlAggrFuncStepHelper,
+ mozStorageSqlAggrFuncFinalHelper);
+ if (srv != SQLITE_OK) {
+ HandleSqliteError(nsnull);
+ return ConvertResultCode(srv);
+ }
+
+ if (mFunctions.Put (aFunctionName, aFunction)) {
+ // We hold function object -- add ref to it
+ NS_ADDREF(aFunction);
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+mozStorageConnection::RemoveFunction(const nsACString &aFunctionName)
+{
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
+ nsISupports *func;
+
+ NS_ENSURE_TRUE(mFunctions.Get (aFunctionName, &func), NS_ERROR_FAILURE);
+
+ int srv = sqlite3_create_function (mDBConn,
+ nsPromiseFlatCString(aFunctionName).get(),
+ 0,
+ SQLITE_ANY,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (srv != SQLITE_OK) {
+ HandleSqliteError(nsnull);
+ return ConvertResultCode(srv);
+ }
+
+ mFunctions.Remove (aFunctionName);
+
+ // We don't hold function object anymore -- remove ref to it
+ NS_RELEASE(func);
+
+ return NS_OK;
+}
+
+int
+mozStorageConnection::s_ProgressHelper(void *arg)
+{
+ mozStorageConnection *_this = static_cast<mozStorageConnection *>(arg);
+ return _this->ProgressHandler();
+}
+
+NS_IMETHODIMP
+mozStorageConnection::SetProgressHandler(PRInt32 aGranularity,
+ mozIStorageProgressHandler *aHandler,
+ mozIStorageProgressHandler **aOldHandler)
+{
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
+ // Return previous one
+ NS_IF_ADDREF(*aOldHandler = mProgressHandler);
+
+ if (!aHandler || aGranularity <= 0) {
+ aHandler = nsnull;
+ aGranularity = 0;
+ }
+ mProgressHandler = aHandler;
+ sqlite3_progress_handler (mDBConn, aGranularity, s_ProgressHelper, this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozStorageConnection::RemoveProgressHandler(mozIStorageProgressHandler **aOldHandler)
+{
+ if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
+
+ // Return previous one
+ NS_IF_ADDREF(*aOldHandler = mProgressHandler);
+
+ mProgressHandler = nsnull;
+ sqlite3_progress_handler (mDBConn, 0, NULL, NULL);
+
+ return NS_OK;
+}
+
+int
+mozStorageConnection::ProgressHandler()
+{
+ if (mProgressHandler) {
+ PRBool res;
+ nsresult rv = mProgressHandler->OnProgress(this, &res);
+ if (NS_FAILED(rv)) return 0; // Don't break request
+ return res ? 1 : 0;
+ }
+ return 0;
}
/**
** Utilities
**/
NS_IMETHODIMP
mozStorageConnection::BackupDB(const nsAString &aFileName,
--- a/storage/src/mozStorageConnection.h
+++ b/storage/src/mozStorageConnection.h
@@ -16,16 +16,17 @@
*
* The Initial Developer of the Original Code is
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -37,16 +38,18 @@
* ***** END LICENSE BLOCK ***** */
#ifndef _MOZSTORAGECONNECTION_H_
#define _MOZSTORAGECONNECTION_H_
#include "nsCOMPtr.h"
#include "nsString.h"
+#include "nsDataHashtable.h"
+#include "mozIStorageProgressHandler.h"
#include "mozIStorageConnection.h"
#include "nsIMutableArray.h"
#include <sqlite3.h>
class nsIFile;
class mozIStorageService;
@@ -65,23 +68,41 @@ public:
// fetch the native handle
sqlite3 *GetNativeConnection() { return mDBConn; }
private:
~mozStorageConnection();
protected:
+ struct FindFuncEnumArgs {
+ nsISupports *mTarget;
+ PRBool mFound;
+ };
+
void HandleSqliteError(const char *aSqlStatement);
+ static PLDHashOperator s_FindFuncEnum(const nsACString &aKey,
+ nsISupports* aData, void* userArg);
+ static PLDHashOperator s_ReleaseFuncEnum(const nsACString &aKey,
+ nsISupports* aData, void* userArg);
+ PRBool FindFunctionByInstance(nsISupports *aInstance);
+
+ static int s_ProgressHelper(void *arg);
+ // Generic progress handler
+ // Dispatch call to registered progress handler,
+ // if there is one. Do nothing in other cases.
+ int ProgressHandler();
sqlite3 *mDBConn;
nsCOMPtr<nsIFile> mDatabaseFile;
PRBool mTransactionInProgress;
- nsCOMPtr<nsIMutableArray> mFunctions;
+ nsDataHashtable<nsCStringHashKey, nsISupports*> mFunctions;
+
+ nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
// This isn't accessed but is used to make sure that the connections do
// not outlive the service. The service, for example, owns certain locks
// in mozStorageAsyncIO file that the connections depend on.
nsCOMPtr<mozIStorageService> mStorageService;
};
#endif /* _MOZSTORAGECONNECTION_H_ */
--- a/storage/test/storage1.cpp
+++ b/storage/test/storage1.cpp
@@ -1,116 +1,244 @@
#include <stdlib.h>
#include <stdio.h>
#include "nsIComponentManager.h"
#include "nsISimpleEnumerator.h"
#include "nsIServiceManager.h"
+#include "nsILocalFile.h"
+#include "nsIVariant.h"
+#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
#include "nsCOMPtr.h"
#include "nsStringAPI.h"
-#include "nsILocalFile.h"
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "mozIStorageValueArray.h"
#include "mozIStorageStatement.h"
#include "mozIStorageFunction.h"
+#include "mozIStorageAggregateFunction.h"
+#include "mozIStorageProgressHandler.h"
#include "mozStorageCID.h"
+#include "nsXPCOMCID.h"
+
#define TEST_CHECK_ERROR(rv) \
do { if (NS_FAILED(rv)) { \
dbConn->GetLastError(&gerr); \
dbConn->GetLastErrorString(gerrstr); \
fprintf (stderr, "Error: %d 0x%08x %s\n", gerr, gerr, gerrstr.get()); \
return 0; \
} } while (0)
-#ifdef XP_UNIX
-#define TEST_DB NS_LITERAL_CSTRING("/tmp/foo.sdb")
-#else
#define TEST_DB NS_LITERAL_CSTRING("foo.sdb")
-#endif
int gerr;
nsCString gerrstr;
class TestFunc : public mozIStorageFunction {
public:
TestFunc() { }
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
};
NS_IMPL_ISUPPORTS1(TestFunc, mozIStorageFunction)
NS_IMETHODIMP
-TestFunc::OnFunctionCall (mozIStorageValueArray *sva)
+TestFunc::OnFunctionCall (mozIStorageValueArray *sva, nsIVariant **_retval)
+{
+ nsCOMPtr<nsIWritableVariant> outVar;
+
+ fprintf (stderr, "* function call!\n");
+
+ outVar = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if(!outVar)
+ return NS_ERROR_FAILURE;
+ outVar->SetAsInt32(0xDEADBEEF);
+ NS_ADDREF(*_retval = outVar);
+ return NS_OK;
+}
+
+class TestAggregateFunc : public mozIStorageAggregateFunction {
+private:
+ PRInt32 mCalls;
+public:
+ TestAggregateFunc() : mCalls(0) { }
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISTORAGEAGGREGATEFUNCTION
+};
+
+NS_IMPL_ISUPPORTS1(TestAggregateFunc, mozIStorageAggregateFunction)
+
+NS_IMETHODIMP
+TestAggregateFunc::OnStep (mozIStorageValueArray *sva)
{
- fprintf (stderr, "* function call!\n");
+ ++mCalls;
+ fprintf (stderr, "* aggregate step %d!\n", mCalls);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TestAggregateFunc::OnFinal (nsIVariant **_retval)
+{
+ nsCOMPtr<nsIWritableVariant> outVar;
+
+ fprintf (stderr, "* aggregate result %d!\n", - mCalls * mCalls);
+
+ outVar = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if(!outVar)
+ return NS_ERROR_FAILURE;
+ outVar->SetAsInt32(-mCalls * mCalls);
+ NS_ADDREF(*_retval = outVar);
+ return NS_OK;
+}
+
+class TestProgressHandler : public mozIStorageProgressHandler {
+private:
+ PRUint32 mTicks;
+public:
+ TestProgressHandler() : mTicks(0) { }
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISTORAGEPROGRESSHANDLER
+ PRUint32 getTicks() const { return mTicks; }
+};
+
+NS_IMPL_ISUPPORTS1(TestProgressHandler, mozIStorageProgressHandler)
+
+NS_IMETHODIMP
+TestProgressHandler::OnProgress (mozIStorageConnection *aConnection, PRBool *_retval)
+{
+ ++mTicks;
+ *_retval = PR_FALSE;
return NS_OK;
}
int
main (int argc, char **argv)
{
nsresult rv;
+ TestFunc *tf;
+ TestAggregateFunc *taf;
NS_InitXPCOM2(nsnull, nsnull, nsnull);
nsCOMPtr<mozIStorageService> dbSrv;
dbSrv = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIFile> tmpDir;
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = tmpDir->AppendNative(TEST_DB);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCAutoString tmpPath;
+ rv = tmpDir->GetNativePath(tmpPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
nsCOMPtr<nsILocalFile> f;
- rv = NS_NewNativeLocalFile (TEST_DB, PR_FALSE, getter_AddRefs(f));
+ rv = NS_NewNativeLocalFile (tmpPath, PR_FALSE, getter_AddRefs(f));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageConnection> dbConn;
rv = dbSrv->OpenDatabase(f, getter_AddRefs(dbConn));
NS_ENSURE_SUCCESS(rv, rv);
- rv = dbConn->CreateFunction("x_test", -1, new TestFunc());
+ tf = new TestFunc();
+ rv = dbConn->CreateFunction(NS_LITERAL_CSTRING("x_test"), -1, tf);
NS_ENSURE_SUCCESS(rv, rv);
+ // Must be error: function exists
+ rv = dbConn->CreateFunction(NS_LITERAL_CSTRING("x_test"), -1, tf);
+ NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
+
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("SELECT x_test(1)"));
NS_ENSURE_SUCCESS(rv, rv);
+ taf = new TestAggregateFunc();
+ rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr"), -1, taf);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Must be error: function exists with this name
+ rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr"), -1, taf);
+ NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
+
+ // Must be error: function exists with this implementation
+ rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr2"), -1, taf);
+ NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
+
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE foo"));
// TEST_CHECK_ERROR(rv);
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE foo (i INTEGER)"));
TEST_CHECK_ERROR(rv);
nsCOMPtr<mozIStorageStatement> dbFooInsertStatement;
rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("INSERT INTO foo VALUES ( ?1 )"), getter_AddRefs(dbFooInsertStatement));
TEST_CHECK_ERROR(rv);
nsCOMPtr<mozIStorageStatement> dbFooSelectStatement;
rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT i FROM foo"), getter_AddRefs(dbFooSelectStatement));
TEST_CHECK_ERROR(rv);
+ nsCOMPtr<mozIStorageStatement> dbAggrSelectStatement;
+ rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT x_aggr(i) FROM foo"), getter_AddRefs(dbAggrSelectStatement));
+ TEST_CHECK_ERROR(rv);
+
for (int i = 0; i < 10; i++) {
rv = dbFooInsertStatement->BindInt32Parameter (0, i);
TEST_CHECK_ERROR(rv);
rv = dbFooInsertStatement->Execute ();
TEST_CHECK_ERROR(rv);
}
fprintf (stderr, "10 values written to foo...\n");
nsCOMPtr<mozIStorageValueArray> dbRow = do_QueryInterface(dbFooSelectStatement);
PRBool hasMore = PR_FALSE;
while ((dbFooSelectStatement->ExecuteStep(&hasMore) == NS_OK) && hasMore)
{
PRUint32 len;
-
+
dbRow->GetNumEntries (&len);
fprintf (stderr, "Row[length %d]: %d '%s'\n", len, dbRow->AsInt32(0), dbRow->AsSharedUTF8String(0, 0));
}
TEST_CHECK_ERROR(rv);
fprintf (stderr, "Done. %d 0x%08x %p\n", rv, rv, dbRow.get());
+
+ dbRow = do_QueryInterface(dbAggrSelectStatement);
+ hasMore = PR_FALSE;
+
+ while ((dbAggrSelectStatement->ExecuteStep(&hasMore) == NS_OK) && hasMore)
+ {
+ PRUint32 len;
+ dbRow->GetNumEntries (&len);
+ fprintf (stderr, "Row[length %d]: %d '%s'\n", len, dbRow->AsInt32(0), dbRow->AsSharedUTF8String(0, 0));
+ }
+
+ TEST_CHECK_ERROR(rv);
+ fprintf (stderr, "Done. %d 0x%08x %p\n", rv, rv, dbRow.get());
+
+ TestProgressHandler *tph = new TestProgressHandler();
+ nsCOMPtr<mozIStorageProgressHandler> oldHandler;
+ rv = dbConn->SetProgressHandler(1, tph, getter_AddRefs(oldHandler));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIStorageStatement> dbSortSelectStatement;
+ rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT i FROM foo ORDER BY i DESC"), getter_AddRefs(dbSortSelectStatement));
+ TEST_CHECK_ERROR(rv);
+
+ rv = dbSortSelectStatement->ExecuteStep(&hasMore);
+ TEST_CHECK_ERROR(rv);
+ fprintf (stderr, "Statement execution takes %d ticks\n", tph->getTicks());
+
+ return 0;
}
--- a/storage/test/unit/head_storage.js
+++ b/storage/test/unit/head_storage.js
@@ -60,17 +60,17 @@ function cleanup()
function getService()
{
return Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
}
var gDBConn = null;
function getOpenedDatabase()
{
- return gDBConn ? gDBConn : getService().openDatabase(getTestDB());
+ return gDBConn ? gDBConn : gDBConn = getService().openDatabase(getTestDB());
}
function createStatement(aSQL)
{
return getOpenedDatabase().createStatement(aSQL);
}
cleanup();
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/test_storage_aggregates.js
@@ -0,0 +1,143 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This file tests the custom aggregate functions
+
+var testNums = [1, 2, 3, 4];
+
+function setup()
+{
+ getOpenedDatabase().createTable("function_tests", "id INTEGER PRIMARY KEY");
+
+ var stmt = createStatement("INSERT INTO function_tests (id) VALUES(?1)");
+ for(var i = 0; i < testNums.length; ++i) {
+ stmt.bindInt32Parameter(0, testNums[i]);
+ stmt.execute();
+ }
+ stmt.reset();
+}
+
+var testSquareAndSumFunction = {
+ calls: 0,
+ _sas: 0,
+
+ reset: function() {
+ this.calls = 0;
+ this._sas = 0;
+ },
+
+ onStep: function(val) {
+ ++this.calls;
+ this._sas += val.getInt32(0) * val.getInt32(0);
+ },
+
+ onFinal: function() {
+ var retval = this._sas;
+ this._sas = 0; // Prepare for next group
+ return retval;
+ }
+};
+
+function test_aggregate_registration()
+{
+ var msc = getOpenedDatabase();
+ msc.createAggregateFunction("test_sas_aggr", 1, testSquareAndSumFunction);
+}
+
+function test_aggregate_no_double_registration()
+{
+ var msc = getOpenedDatabase();
+ try {
+ msc.createAggregateFunction("test_sas_aggr", 2, testSquareAndSumFunction);
+ do_throw("We shouldn't get here!");
+ } catch (e) {
+ do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
+ }
+}
+
+function test_aggregate_removal()
+{
+ var msc = getOpenedDatabase();
+ msc.removeFunction("test_sas_aggr");
+ // Should be Ok now
+ msc.createAggregateFunction("test_sas_aggr", 1, testSquareAndSumFunction);
+}
+
+function test_aggregate_no_aliases()
+{
+ var msc = getOpenedDatabase();
+ try {
+ msc.createAggregateFunction("test_sas_aggr2", 1, testSquareAndSumFunction);
+ do_throw("We shouldn't get here!");
+ } catch (e) {
+ do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
+ }
+}
+
+function test_aggregate_call()
+{
+ var stmt = createStatement("SELECT test_sas_aggr(id) FROM function_tests");
+ while(stmt.executeStep());
+ do_check_eq(testNums.length, testSquareAndSumFunction.calls);
+ testSquareAndSumFunction.reset();
+}
+
+function test_aggregate_result()
+{
+ var sas = 0;
+ for(var i = 0; i < testNums.length; ++i) {
+ sas += testNums[i] * testNums[i];
+ }
+ var stmt = createStatement("SELECT test_sas_aggr(id) FROM function_tests");
+ stmt.executeStep();
+ do_check_eq(sas, stmt.getInt32(0));
+ testSquareAndSumFunction.reset();
+}
+
+var tests = [test_aggregate_registration, test_aggregate_no_double_registration,
+ test_aggregate_removal, test_aggregate_no_aliases, test_aggregate_call,
+ test_aggregate_result];
+
+function run_test()
+{
+ setup();
+
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+
+ cleanup();
+}
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/test_storage_function.js
@@ -0,0 +1,122 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This file tests the custom functions
+
+var testNums = [1, 2, 3, 4];
+
+function setup()
+{
+ getOpenedDatabase().createTable("function_tests", "id INTEGER PRIMARY KEY");
+
+ var stmt = createStatement("INSERT INTO function_tests (id) VALUES(?1)");
+ for(var i = 0; i < testNums.length; ++i) {
+ stmt.bindInt32Parameter(0, testNums[i]);
+ stmt.execute();
+ }
+ stmt.reset();
+}
+
+var testSquareFunction = {
+ calls: 0,
+
+ onFunctionCall: function(val) {
+ ++this.calls;
+ return val.getInt32(0) * val.getInt32(0);
+ }
+};
+
+function test_function_registration()
+{
+ var msc = getOpenedDatabase();
+ msc.createFunction("test_square", 1, testSquareFunction);
+}
+
+function test_function_no_double_registration()
+{
+ var msc = getOpenedDatabase();
+ try {
+ msc.createFunction("test_square", 2, testSquareFunction);
+ do_throw("We shouldn't get here!");
+ } catch (e) {
+ do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
+ }
+}
+
+function test_function_removal()
+{
+ var msc = getOpenedDatabase();
+ msc.removeFunction("test_square");
+ // Should be Ok now
+ msc.createFunction("test_square", 1, testSquareFunction);
+}
+
+function test_function_aliases()
+{
+ var msc = getOpenedDatabase();
+ msc.createFunction("test_square2", 1, testSquareFunction);
+}
+
+function test_function_call()
+{
+ var stmt = createStatement("SELECT test_square(id) FROM function_tests");
+ while(stmt.executeStep());
+ do_check_eq(testNums.length, testSquareFunction.calls);
+ testSquareFunction.calls = 0;
+}
+
+function test_function_result()
+{
+ var stmt = createStatement("SELECT test_square(42) FROM function_tests");
+ stmt.executeStep();
+ do_check_eq(42*42, stmt.getInt32(0));
+ testSquareFunction.calls = 0;
+}
+
+var tests = [test_function_registration, test_function_no_double_registration,
+ test_function_removal, test_function_aliases, test_function_call,
+ test_function_result];
+
+function run_test()
+{
+ setup();
+
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+
+ cleanup();
+}
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/test_storage_progresshandler.js
@@ -0,0 +1,128 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Lev Serebryakov <lev@serebryakov.spb.ru>
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This file tests the custom progress handlers
+
+function setup()
+{
+ var msc = getOpenedDatabase();
+ msc.createTable("handler_tests", "id INTEGER PRIMARY KEY, num INTEGER");
+ msc.beginTransaction();
+
+ var stmt = createStatement("INSERT INTO handler_tests (id, num) VALUES(?1, ?2)");
+ for(var i = 0; i < 100; ++i) {
+ stmt.bindInt32Parameter(0, i);
+ stmt.bindInt32Parameter(1, Math.floor(Math.random()*1000));
+ stmt.execute();
+ }
+ stmt.reset();
+ msc.commitTransaction();
+}
+
+var testProgressHandler = {
+ calls: 0,
+ abort: false,
+
+ onProgress: function(comm) {
+ ++this.calls;
+ return this.abort;
+ }
+};
+
+function test_handler_registration()
+{
+ var msc = getOpenedDatabase();
+ msc.setProgressHandler(10, testProgressHandler);
+}
+
+function test_handler_return()
+{
+ var msc = getOpenedDatabase();
+ var oldH = msc.setProgressHandler(5, testProgressHandler);
+ do_check_true(oldH instanceof Ci.mozIStorageProgressHandler);
+}
+
+function test_handler_removal()
+{
+ var msc = getOpenedDatabase();
+ msc.removeProgressHandler();
+ var oldH = msc.removeProgressHandler();
+ do_check_eq(oldH, null);
+}
+
+function test_handler_call()
+{
+ var msc = getOpenedDatabase();
+ msc.setProgressHandler(50, testProgressHandler);
+ // Some long-executing request
+ var stmt = createStatement(
+ "SELECT SUM(t1.num * t2.num) FROM handler_tests AS t1, handler_tests AS t2");
+ while(stmt.executeStep());
+ do_check_true(testProgressHandler.calls > 0);
+}
+
+function test_handler_abort()
+{
+ var msc = getOpenedDatabase();
+ testProgressHandler.abort = true;
+ msc.setProgressHandler(50, testProgressHandler);
+ // Some long-executing request
+ var stmt = createStatement(
+ "SELECT SUM(t1.num * t2.num) FROM handler_tests AS t1, handler_tests AS t2");
+ try {
+ while(stmt.executeStep());
+ do_throw("We shouldn't get here!");
+ } catch (e) {
+ do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
+ // Magic value: callback abort
+ do_check_eq(msc.lastError, 4);
+ }
+}
+
+var tests = [test_handler_registration, test_handler_return,
+ test_handler_removal, test_handler_call,
+ test_handler_abort];
+
+function run_test()
+{
+ setup();
+
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+
+ cleanup();
+}