Bug 333848 - add full-featured user-defined functions and progress handlers to storage. Patch by Lev Serebryakov <blacklion@gmail.com>. r=sdwilsh

This commit is contained in:
sdwilsh@shawnwilsher.com 2007-07-07 14:14:51 -07:00
Родитель b2eeef84ef
Коммит 7f9c748719
12 изменённых файлов: 1103 добавлений и 54 удалений

Просмотреть файл

@ -21,6 +21,7 @@
#
# 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"),
@ -50,7 +51,9 @@ GRE_MODULE = 1
XPIDLSRCS = \
mozIStorageService.idl \
mozIStorageConnection.idl \
mozIStorageAggregateFunction.idl \
mozIStorageFunction.idl \
mozIStorageProgressHandler.idl \
mozIStorageStatement.idl \
mozIStorageStatementWrapper.idl \
mozIStorageDataSet.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();
};

Просмотреть файл

@ -23,6 +23,7 @@
* 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
@ -40,7 +41,9 @@
#include "nsISupports.idl"
interface mozIStorageAggregateFunction;
interface mozIStorageFunction;
interface mozIStorageProgressHandler;
interface mozIStorageStatement;
interface nsIFile;
@ -51,7 +54,7 @@ interface nsIFile;
* 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
@ -196,11 +199,57 @@ interface mozIStorageConnection : nsISupports {
/**
* 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
*/

Просмотреть файл

@ -21,6 +21,7 @@
*
* 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
@ -41,21 +42,32 @@
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);
};

Просмотреть файл

@ -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);
};

Просмотреть файл

@ -24,6 +24,7 @@
* 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
@ -43,8 +44,12 @@
#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"
@ -64,21 +69,26 @@ 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);
}
}
@ -131,10 +141,10 @@ mozStorageConnection::Initialize(nsIFile *aDatabaseFile)
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 {
@ -143,7 +153,7 @@ mozStorageConnection::Initialize(nsIFile *aDatabaseFile)
if (stmt != nsnull)
sqlite3_finalize (stmt);
if (srv != SQLITE_OK) {
sqlite3_close (mDBConn);
mDBConn = nsnull;
@ -154,9 +164,6 @@ mozStorageConnection::Initialize(nsIFile *aDatabaseFile)
return ConvertResultCode(srv);
}
mFunctions = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
@ -178,7 +185,7 @@ mozStorageConnection::GetConnectionReady(PRBool *aConnectionReady)
NS_IMETHODIMP
mozStorageConnection::GetDatabaseFile(nsIFile **aFile)
{
NS_ASSERTION(mDBConn, "connection not initialized");
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
NS_IF_ADDREF(*aFile = mDatabaseFile);
@ -188,7 +195,7 @@ mozStorageConnection::GetDatabaseFile(nsIFile **aFile)
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;
@ -199,7 +206,7 @@ mozStorageConnection::GetLastInsertRowID(PRInt64 *aLastInsertRowID)
NS_IMETHODIMP
mozStorageConnection::GetLastError(PRInt32 *aLastError)
{
NS_ASSERTION(mDBConn, "connection not initialized");
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
*aLastError = sqlite3_errcode(mDBConn);
@ -209,7 +216,7 @@ mozStorageConnection::GetLastError(PRInt32 *aLastError)
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);
@ -220,7 +227,7 @@ mozStorageConnection::GetLastErrorString(nsACString& aLastErrorString)
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(
@ -238,8 +245,8 @@ mozStorageConnection::GetSchemaVersion(PRInt32 *version)
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);
@ -255,7 +262,7 @@ 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)
@ -275,7 +282,7 @@ mozStorageConnection::CreateStatement(const nsACString& aSQLStatement,
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);
@ -290,7 +297,7 @@ mozStorageConnection::ExecuteSimpleSQL(const nsACString& aSQLStatement)
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);
@ -325,7 +332,7 @@ mozStorageConnection::TableExists(const nsACString& aSQLStatement, PRBool *_retv
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);
@ -451,6 +458,130 @@ mozStorageConnection::CreateTable(/*const nsID& aID,*/
** 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,
@ -458,43 +589,227 @@ mozStorageSqlFuncHelper (sqlite3_context *ctx,
{
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;
}
/**

Просмотреть файл

@ -21,6 +21,7 @@
*
* 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
@ -42,6 +43,8 @@
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsDataHashtable.h"
#include "mozIStorageProgressHandler.h"
#include "mozIStorageConnection.h"
#include "nsIMutableArray.h"
@ -70,13 +73,31 @@ 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

Просмотреть файл

@ -5,19 +5,27 @@
#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); \
@ -26,11 +34,7 @@
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;
@ -45,9 +49,71 @@ public:
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)
{
++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;
}
@ -55,6 +121,8 @@ int
main (int argc, char **argv)
{
nsresult rv;
TestFunc *tf;
TestAggregateFunc *taf;
NS_InitXPCOM2(nsnull, nsnull, nsnull);
@ -62,20 +130,48 @@ main (int argc, char **argv)
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);
@ -90,6 +186,10 @@ main (int argc, char **argv)
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);
@ -106,11 +206,39 @@ main (int argc, char **argv)
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;
}

Просмотреть файл

@ -65,7 +65,7 @@ function getService()
var gDBConn = null;
function getOpenedDatabase()
{
return gDBConn ? gDBConn : getService().openDatabase(getTestDB());
return gDBConn ? gDBConn : gDBConn = getService().openDatabase(getTestDB());
}
function createStatement(aSQL)

Просмотреть файл

@ -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();
}

Просмотреть файл

@ -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();
}

Просмотреть файл

@ -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();
}