зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
a884abd3f8
Коммит
ae472131bb
|
@ -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();
|
||||
}
|
Загрузка…
Ссылка в новой задаче