mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 507414 - Add a createAsyncStatement API on mozIStorageConnection. r=sdwilsh, sr=vlad.
--HG-- rename : storage/src/mozStorageStatementJSHelper.cpp => storage/src/mozStorageAsyncStatementJSHelper.cpp rename : storage/src/mozStorageStatementJSHelper.h => storage/src/mozStorageAsyncStatementJSHelper.h rename : storage/src/mozStorageStatementParams.cpp => storage/src/mozStorageAsyncStatementParams.cpp rename : storage/src/mozStorageStatementParams.h => storage/src/mozStorageAsyncStatementParams.h
This commit is contained in:
parent
f6e896ba0c
commit
805fe0e096
@ -65,6 +65,8 @@ XPIDLSRCS = \
|
||||
mozIStorageBindingParamsArray.idl \
|
||||
mozIStorageBindingParams.idl \
|
||||
mozIStorageCompletionCallback.idl \
|
||||
mozIStorageBaseStatement.idl \
|
||||
mozIStorageAsyncStatement.idl \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_NAMESPACES = mozilla
|
||||
|
69
storage/public/mozIStorageAsyncStatement.idl
Normal file
69
storage/public/mozIStorageAsyncStatement.idl
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 "mozIStorageBaseStatement.idl"
|
||||
|
||||
/**
|
||||
* An asynchronous SQL statement. This differs from mozIStorageStatement by
|
||||
* only being usable for asynchronous execution. (mozIStorageStatement can
|
||||
* be used for both synchronous and asynchronous purposes.) This specialization
|
||||
* for asynchronous operation allows us to avoid needing to acquire
|
||||
* synchronization primitives also used by the asynchronous execution thread.
|
||||
* In contrast, mozIStorageStatement may need to acquire the primitives and
|
||||
* consequently can cause the main thread to lock for extended intervals while
|
||||
* the asynchronous thread performs some long-running operation.
|
||||
*/
|
||||
[scriptable, uuid(2400f64d-2cb3-49a9-b01e-f03cacb8aa6e)]
|
||||
interface mozIStorageAsyncStatement : mozIStorageBaseStatement {
|
||||
/*
|
||||
* 'params' provides a magic JS helper that lets you assign parameters by
|
||||
* name. Unlike the helper on mozIStorageStatement, you cannot enumerate
|
||||
* in order to find out what parameters are legal.
|
||||
*
|
||||
* This does not work for BLOBs. You must use an explicit binding API for
|
||||
* that.
|
||||
*
|
||||
* example:
|
||||
* stmt.params.foo = 1;
|
||||
* stmt.params["bar"] = 2;
|
||||
* let argName = "baz";
|
||||
* stmt.params[argName] = 3;
|
||||
*
|
||||
* readonly attribute nsIMagic params;
|
||||
*/
|
||||
};
|
182
storage/public/mozIStorageBaseStatement.idl
Normal file
182
storage/public/mozIStorageBaseStatement.idl
Normal file
@ -0,0 +1,182 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et
|
||||
* ***** 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 mozStorage.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
* Shawn Wilsher <me@shawnwilsher.com>
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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"
|
||||
#include "mozIStorageBindingParams.idl"
|
||||
|
||||
interface mozIStorageConnection;
|
||||
interface mozIStorageStatementCallback;
|
||||
interface mozIStoragePendingStatement;
|
||||
interface mozIStorageBindingParams;
|
||||
interface mozIStorageBindingParamsArray;
|
||||
|
||||
/**
|
||||
* The base interface for both pure asynchronous storage statements
|
||||
* (mozIStorageAsyncStatement) and 'classic' storage statements
|
||||
* (mozIStorageStatement) that can be used for both synchronous and asynchronous
|
||||
* purposes.
|
||||
*/
|
||||
[scriptable, uuid(da2ec336-fbbb-4ba1-9778-8c9825980d01)]
|
||||
interface mozIStorageBaseStatement : mozIStorageBindingParams {
|
||||
/**
|
||||
* Finalizes a statement so you can successfully close a database connection.
|
||||
* Once a statement has been finalized it can no longer be used for any
|
||||
* purpose.
|
||||
*
|
||||
* Statements are implicitly finalized when their reference counts hits zero.
|
||||
* If you are a native (C++) caller this is accomplished by setting all of
|
||||
* your nsCOMPtr instances to be NULL. If you are operating from JavaScript
|
||||
* code then you cannot rely on this behavior because of the involvement of
|
||||
* garbage collection.
|
||||
*
|
||||
* When finalizing an asynchronous statement you do not need to worry about
|
||||
* whether the statement has actually been executed by the asynchronous
|
||||
* thread; you just need to call finalize after your last call to executeAsync
|
||||
* involving the statement. However, you do need to use asyncClose instead of
|
||||
* close on the connection if any statements have been used asynchronously.
|
||||
*/
|
||||
void finalize();
|
||||
|
||||
/**
|
||||
* Bind the given value at the given numeric index.
|
||||
*
|
||||
* @param aParamIndex
|
||||
* 0-based index, 0 corresponding to the first numbered argument or
|
||||
* "?1".
|
||||
* @param aValue
|
||||
* Argument value.
|
||||
* @param aValueSize
|
||||
* Length of aValue in bytes.
|
||||
* @{
|
||||
*/
|
||||
[deprecated] void bindUTF8StringParameter(in unsigned long aParamIndex,
|
||||
in AUTF8String aValue);
|
||||
[deprecated] void bindStringParameter(in unsigned long aParamIndex,
|
||||
in AString aValue);
|
||||
[deprecated] void bindDoubleParameter(in unsigned long aParamIndex,
|
||||
in double aValue);
|
||||
[deprecated] void bindInt32Parameter(in unsigned long aParamIndex,
|
||||
in long aValue);
|
||||
[deprecated] void bindInt64Parameter(in unsigned long aParamIndex,
|
||||
in long long aValue);
|
||||
[deprecated] void bindNullParameter(in unsigned long aParamIndex);
|
||||
[deprecated] void bindBlobParameter(
|
||||
in unsigned long aParamIndex,
|
||||
[array,const,size_is(aValueSize)] in octet aValue,
|
||||
in unsigned long aValueSize);
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Binds the array of parameters to the statement. When executeAsync is
|
||||
* called, all the parameters in aParameters are bound and then executed.
|
||||
*
|
||||
* @param aParameters
|
||||
* The array of parameters to bind to the statement upon execution.
|
||||
*
|
||||
* @note This is only works on statements being used asynchronously.
|
||||
*/
|
||||
void bindParameters(in mozIStorageBindingParamsArray aParameters);
|
||||
|
||||
/**
|
||||
* Creates a new mozIStorageBindingParamsArray that can be used to bind
|
||||
* multiple sets of data to a statement with bindParameters.
|
||||
*
|
||||
* @return a mozIStorageBindingParamsArray that multiple sets of parameters
|
||||
* can be bound to.
|
||||
*
|
||||
* @note This is only useful for statements being used asynchronously.
|
||||
*/
|
||||
mozIStorageBindingParamsArray newBindingParamsArray();
|
||||
|
||||
/**
|
||||
* Execute a query asynchronously using any currently bound parameters. This
|
||||
* statement can be reused immediately, and reset does not need to be called.
|
||||
*
|
||||
* @note If you have any custom defined functions, they must be re-entrant
|
||||
* since they can be called on multiple threads.
|
||||
*
|
||||
* @param aCallback [optional]
|
||||
* The callback object that will be notified of progress, errors, and
|
||||
* completion.
|
||||
* @return an object that can be used to cancel the statements execution.
|
||||
*/
|
||||
mozIStoragePendingStatement executeAsync(
|
||||
[optional] in mozIStorageStatementCallback aCallback
|
||||
);
|
||||
|
||||
/**
|
||||
* The statement is not usable, either because it failed to initialize or
|
||||
* was explicitly finalized.
|
||||
*/
|
||||
const long MOZ_STORAGE_STATEMENT_INVALID = 0;
|
||||
/**
|
||||
* The statement is usable.
|
||||
*/
|
||||
const long MOZ_STORAGE_STATEMENT_READY = 1;
|
||||
/**
|
||||
* Indicates that the statement is executing and the row getters may be used.
|
||||
*
|
||||
* @note This is only relevant for mozIStorageStatement instances being used
|
||||
* in a synchronous fashion.
|
||||
*/
|
||||
const long MOZ_STORAGE_STATEMENT_EXECUTING = 2;
|
||||
|
||||
/**
|
||||
* Find out whether the statement is usable (has not been finalized).
|
||||
*/
|
||||
readonly attribute long state;
|
||||
|
||||
/**
|
||||
* Escape a string for SQL LIKE search.
|
||||
*
|
||||
* @note Consumers will have to use same escape char when doing statements
|
||||
* such as: ...LIKE '?1' ESCAPE '/'...
|
||||
*
|
||||
* @param aValue
|
||||
* The string to escape for SQL LIKE.
|
||||
* @param aEscapeChar
|
||||
* The escape character.
|
||||
* @return an AString of an escaped version of aValue
|
||||
* (%, _ and the escape char are escaped with the escape char)
|
||||
* For example, we will convert "foo/bar_baz%20cheese"
|
||||
* into "foo//bar/_baz/%20cheese" (if the escape char is '/').
|
||||
*/
|
||||
AString escapeStringForLIKE(in AString aValue, in wchar aEscapeChar);
|
||||
};
|
@ -45,7 +45,9 @@ interface mozIStorageAggregateFunction;
|
||||
interface mozIStorageCompletionCallback;
|
||||
interface mozIStorageFunction;
|
||||
interface mozIStorageProgressHandler;
|
||||
interface mozIStorageBaseStatement;
|
||||
interface mozIStorageStatement;
|
||||
interface mozIStorageAsyncStatement;
|
||||
interface mozIStorageStatementCallback;
|
||||
interface mozIStoragePendingStatement;
|
||||
interface nsIFile;
|
||||
@ -59,11 +61,10 @@ interface nsIFile;
|
||||
*
|
||||
* @threadsafe
|
||||
*/
|
||||
[scriptable, uuid(5a06b207-1977-47d4-b140-271cb851bb26)]
|
||||
[scriptable, uuid(32b43ec6-e905-4070-9cfe-370c45f7c1bf)]
|
||||
interface mozIStorageConnection : nsISupports {
|
||||
/*
|
||||
* Initialization and status
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Initialization and status
|
||||
|
||||
/**
|
||||
* Closes a database connection. Callers must finalize all statements created
|
||||
@ -125,9 +126,8 @@ interface mozIStorageConnection : nsISupports {
|
||||
*/
|
||||
attribute long schemaVersion;
|
||||
|
||||
/*
|
||||
* Statement creation
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Statement creation
|
||||
|
||||
/**
|
||||
* Create a mozIStorageStatement for the given SQL expression. The
|
||||
@ -135,12 +135,28 @@ interface mozIStorageConnection : nsISupports {
|
||||
* ?1, ?2 etc. to indicate specific numbered arguments or :name and
|
||||
* $var to indicate named arguments.
|
||||
*
|
||||
* @param aSQLStatement The SQL statement to execute
|
||||
*
|
||||
* @returns a new mozIStorageStatement
|
||||
* @param aSQLStatement
|
||||
* The SQL statement to execute.
|
||||
* @return a new mozIStorageStatement
|
||||
*/
|
||||
mozIStorageStatement createStatement(in AUTF8String aSQLStatement);
|
||||
|
||||
/**
|
||||
* Create an asynchronous statement (mozIStorageAsyncStatement) for the given
|
||||
* SQL expression. An asynchronous statement can only be used to dispatch
|
||||
* asynchronous requests to the asynchronous execution thread and cannot be
|
||||
* used to take any synchronous actions on the database.
|
||||
*
|
||||
* The expression may use ? to indicate sequential numbered arguments,
|
||||
* ?1, ?2 etc. to indicate specific numbered arguments or :name and
|
||||
* $var to indicate named arguments.
|
||||
*
|
||||
* @param aSQLStatement
|
||||
* The SQL statement to execute.
|
||||
* @return a new mozIStorageAsyncStatement
|
||||
*/
|
||||
mozIStorageAsyncStatement createAsyncStatement(in AUTF8String aSQLStatement);
|
||||
|
||||
/**
|
||||
* Execute a SQL expression, expecting no arguments.
|
||||
*
|
||||
@ -165,10 +181,10 @@ interface mozIStorageConnection : nsISupports {
|
||||
* @param aCallback [optional]
|
||||
* The callback object that will be notified of progress, errors, and
|
||||
* completion.
|
||||
* @returns an object that can be used to cancel the statements execution.
|
||||
* @return an object that can be used to cancel the statements execution.
|
||||
*/
|
||||
mozIStoragePendingStatement executeAsync(
|
||||
[array, size_is(aNumStatements)] in mozIStorageStatement aStatements,
|
||||
[array, size_is(aNumStatements)] in mozIStorageBaseStatement aStatements,
|
||||
in unsigned long aNumStatements,
|
||||
[optional] in mozIStorageStatementCallback aCallback
|
||||
);
|
||||
@ -176,8 +192,9 @@ interface mozIStorageConnection : nsISupports {
|
||||
/**
|
||||
* Check if the given table exists.
|
||||
*
|
||||
* @param aTableName The table to check
|
||||
* @returns TRUE if table exists, FALSE otherwise.
|
||||
* @param aTableName
|
||||
* The table to check
|
||||
* @return TRUE if table exists, FALSE otherwise.
|
||||
*/
|
||||
boolean tableExists(in AUTF8String aTableName);
|
||||
|
||||
@ -185,14 +202,12 @@ interface mozIStorageConnection : nsISupports {
|
||||
* Check if the given index exists.
|
||||
*
|
||||
* @param aIndexName The index to check
|
||||
* @returns TRUE if the index exists, FALSE otherwise.
|
||||
* @return TRUE if the index exists, FALSE otherwise.
|
||||
*/
|
||||
boolean indexExists(in AUTF8String aIndexName);
|
||||
|
||||
|
||||
/*
|
||||
* Transactions
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Transactions
|
||||
|
||||
/**
|
||||
* Returns true if a transaction is active on this connection.
|
||||
@ -225,9 +240,8 @@ interface mozIStorageConnection : nsISupports {
|
||||
*/
|
||||
void rollbackTransaction();
|
||||
|
||||
/*
|
||||
* Tables
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Tables
|
||||
|
||||
/**
|
||||
* Create the table with the given name and schema.
|
||||
@ -236,34 +250,36 @@ interface mozIStorageConnection : nsISupports {
|
||||
* (XXX at some point in the future it will check if the schema is
|
||||
* the same as what is specified, but that doesn't happen currently.)
|
||||
*
|
||||
* @param aTableName the table name to be created, consisting of
|
||||
* [A-Za-z0-9_], and beginning with a letter.
|
||||
*
|
||||
* @param aTableSchema the schema of the table; what would normally
|
||||
* go between the parens in a CREATE TABLE statement: e.g., "foo
|
||||
* INTEGER, bar STRING".
|
||||
*
|
||||
* @throws NS_ERROR_FAILURE if the table already exists or could not
|
||||
* be created for any other reason.
|
||||
* @param aTableName
|
||||
* The table name to be created, consisting of [A-Za-z0-9_], and
|
||||
* beginning with a letter.
|
||||
* @param aTableSchema
|
||||
* The schema of the table; what would normally go between the parens
|
||||
* in a CREATE TABLE statement: e.g., "foo INTEGER, bar STRING".
|
||||
*
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* If the table already exists or could not be created for any other
|
||||
* reason.
|
||||
*/
|
||||
void createTable(in string aTableName,
|
||||
in string aTableSchema);
|
||||
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Functions
|
||||
|
||||
/**
|
||||
* Create a new SQL function. If you use your connection on multiple threads,
|
||||
* your function needs to be threadsafe, or it should only be called on one
|
||||
* thread.
|
||||
*
|
||||
* @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.
|
||||
* @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 AUTF8String aFunctionName,
|
||||
in long aNumArguments,
|
||||
@ -274,12 +290,14 @@ interface mozIStorageConnection : nsISupports {
|
||||
* multiple threads, your function needs to be threadsafe, or it should only
|
||||
* be called on one thread.
|
||||
*
|
||||
* @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.
|
||||
* @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,
|
||||
@ -287,7 +305,8 @@ interface mozIStorageConnection : nsISupports {
|
||||
/**
|
||||
* Delete custom SQL function (simple or aggregate one).
|
||||
*
|
||||
* @param aFunctionName The name of function to remove.
|
||||
* @param aFunctionName
|
||||
* The name of function to remove.
|
||||
*/
|
||||
void removeFunction(in AUTF8String aFunctionName);
|
||||
|
||||
@ -297,10 +316,11 @@ interface mozIStorageConnection : nsISupports {
|
||||
* handler should be threadsafe if you use this connection object on more than
|
||||
* one thread.
|
||||
*
|
||||
* @param aGranularity The number of SQL virtual machine steps between
|
||||
* progress handler callbacks.
|
||||
* @param aHandler The instance of mozIStorageProgressHandler.
|
||||
*
|
||||
* @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,
|
||||
|
@ -158,6 +158,12 @@ interface mozIStorageError : nsISupports {
|
||||
*/
|
||||
const long FORMAT = 24;
|
||||
|
||||
/**
|
||||
* Attempt to bind a parameter using an out-of-range index or nonexistent
|
||||
* named parameter name.
|
||||
*/
|
||||
const long RANGE = 25;
|
||||
|
||||
/**
|
||||
* File opened that is not a database file.
|
||||
*/
|
||||
|
@ -39,8 +39,9 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
#include "mozIStorageValueArray.idl"
|
||||
|
||||
interface mozIStorageConnection;
|
||||
interface mozIStorageValueArray;
|
||||
interface nsIArray;
|
||||
interface nsIVariant;
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
* Shawn Wilsher <me@shawnwilsher.com>
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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
|
||||
@ -37,23 +39,16 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "mozIStorageValueArray.idl"
|
||||
#include "mozIStorageBaseStatement.idl"
|
||||
|
||||
interface mozIStorageConnection;
|
||||
interface mozIStorageStatementCallback;
|
||||
interface mozIStoragePendingStatement;
|
||||
interface mozIStorageBindingParamsArray;
|
||||
|
||||
[scriptable, uuid(20c45bdd-51d4-4f07-b70e-5feaa6302197)]
|
||||
interface mozIStorageStatement : mozIStorageValueArray {
|
||||
/**
|
||||
* Finalizes a statement so you can successfully close a database connection.
|
||||
* This method does not need to be used from native callers since you can just
|
||||
* set the statement to null, but is extremely useful to JS callers.
|
||||
*/
|
||||
void finalize();
|
||||
[ptr] native octetPtr(PRUint8);
|
||||
|
||||
/**
|
||||
* A SQL statement that can be used for both synchronous and asynchronous
|
||||
* purposes.
|
||||
*/
|
||||
[scriptable, uuid(57ec7be1-36cf-4510-b938-7d1c9ee8cec5)]
|
||||
interface mozIStorageStatement : mozIStorageBaseStatement {
|
||||
/**
|
||||
* Create a clone of this statement, by initializing a new statement
|
||||
* with the same connection and same SQL statement as this one. It
|
||||
@ -79,7 +74,7 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
* @param aName
|
||||
* The name of the parameter you want the index for. This does not
|
||||
* include the leading ':'.
|
||||
* @returns the index of the named parameter.
|
||||
* @return the index of the named parameter.
|
||||
*/
|
||||
unsigned long getParameterIndex(in AUTF8String aName);
|
||||
|
||||
@ -96,7 +91,8 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
/**
|
||||
* Obtains the index of the column with the specified name.
|
||||
*
|
||||
* @param aName The name of the column.
|
||||
* @param aName
|
||||
* The name of the column.
|
||||
* @return The index of the column with the specified name.
|
||||
*/
|
||||
unsigned long getColumnIndex(in AUTF8String aName);
|
||||
@ -104,9 +100,10 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
/**
|
||||
* Obtains the declared column type of a prepared statement.
|
||||
*
|
||||
* @param aParamIndex The zero-based index of the column who's declared type
|
||||
* we are interested in.
|
||||
* @returns the declared index type.
|
||||
* @param aParamIndex
|
||||
* The zero-based index of the column who's declared type we are
|
||||
* interested in.
|
||||
* @return the declared index type.
|
||||
*/
|
||||
AUTF8String getColumnDecltype(in unsigned long aParamIndex);
|
||||
|
||||
@ -115,39 +112,6 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Bind the given value to the parameter at aParamIndex. Index 0
|
||||
* denotes the first numbered argument or ?1.
|
||||
*/
|
||||
void bindUTF8StringParameter(in unsigned long aParamIndex,
|
||||
in AUTF8String aValue);
|
||||
void bindStringParameter(in unsigned long aParamIndex, in AString aValue);
|
||||
void bindDoubleParameter(in unsigned long aParamIndex, in double aValue);
|
||||
void bindInt32Parameter(in unsigned long aParamIndex, in long aValue);
|
||||
void bindInt64Parameter(in unsigned long aParamIndex, in long long aValue);
|
||||
void bindNullParameter(in unsigned long aParamIndex);
|
||||
void bindBlobParameter(in unsigned long aParamIndex,
|
||||
[array,const,size_is(aValueSize)] in octet aValue,
|
||||
in unsigned long aValueSize);
|
||||
|
||||
/**
|
||||
* Binds the array of parameters to the statement. When executeAsync is
|
||||
* called, all the parameters in aParameters are bound and then executed.
|
||||
*
|
||||
* @param aParameters
|
||||
* The array of parameters to bind to the statement upon execution.
|
||||
*/
|
||||
void bindParameters(in mozIStorageBindingParamsArray aParameters);
|
||||
|
||||
/**
|
||||
* Creates a new mozIStorageBindingParamsArray that can be used to bind
|
||||
* multiple sets of data to a statement.
|
||||
*
|
||||
* @returns a mozIStorageBindingParamsArray that multiple sets of parameters
|
||||
* can be bound to.
|
||||
*/
|
||||
mozIStorageBindingParamsArray newBindingParamsArray();
|
||||
|
||||
/**
|
||||
* Execute the query, ignoring any results. This is accomplished by
|
||||
* calling executeStep() once, and then calling reset().
|
||||
@ -162,10 +126,9 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
* must be called on the statement after the last call of
|
||||
* executeStep.
|
||||
*
|
||||
* @returns a boolean indicating whether there are more rows or not;
|
||||
* row data may be accessed using mozIStorageValueArray methods on
|
||||
* the statement.
|
||||
*
|
||||
* @return a boolean indicating whether there are more rows or not;
|
||||
* row data may be accessed using mozIStorageValueArray methods on
|
||||
* the statement.
|
||||
*/
|
||||
boolean executeStep();
|
||||
|
||||
@ -176,7 +139,7 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
*
|
||||
* @deprecated As of Mozilla 1.9.2 in favor of executeStep().
|
||||
*
|
||||
* @returns a boolean indicating whether there are more rows or not.
|
||||
* @return a boolean indicating whether there are more rows or not.
|
||||
*
|
||||
* [deprecated] boolean step();
|
||||
*/
|
||||
@ -195,43 +158,152 @@ interface mozIStorageStatement : mozIStorageValueArray {
|
||||
* readonly attribute mozIStorageStatementRow row;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute a query asynchronously using any currently bound parameters. This
|
||||
* statement can be reused immediately, and reset does not need to be called.
|
||||
*
|
||||
* Note: If you have any custom defined functions, they must be re-entrant
|
||||
* since they can be called on multiple threads.
|
||||
*
|
||||
* @param aCallback [optional]
|
||||
* The callback object that will be notified of progress, errors, and
|
||||
* completion.
|
||||
* @returns an object that can be used to cancel the statements execution.
|
||||
*/
|
||||
mozIStoragePendingStatement executeAsync(
|
||||
[optional] in mozIStorageStatementCallback aCallback
|
||||
);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Copied contents of mozIStorageValueArray
|
||||
|
||||
/**
|
||||
* The current state. Row getters are only valid while
|
||||
* the statement is in the "executing" state.
|
||||
* These type values are returned by getTypeOfIndex
|
||||
* to indicate what type of value is present at
|
||||
* a given column.
|
||||
*/
|
||||
const long MOZ_STORAGE_STATEMENT_INVALID = 0;
|
||||
const long MOZ_STORAGE_STATEMENT_READY = 1;
|
||||
const long MOZ_STORAGE_STATEMENT_EXECUTING = 2;
|
||||
|
||||
readonly attribute long state;
|
||||
const long VALUE_TYPE_NULL = 0;
|
||||
const long VALUE_TYPE_INTEGER = 1;
|
||||
const long VALUE_TYPE_FLOAT = 2;
|
||||
const long VALUE_TYPE_TEXT = 3;
|
||||
const long VALUE_TYPE_BLOB = 4;
|
||||
|
||||
/**
|
||||
* Escape a string for SQL LIKE search.
|
||||
*
|
||||
* @param aValue the string to escape for SQL LIKE
|
||||
* @param aEscapeChar the escape character
|
||||
* @returns an AString of an escaped version of aValue
|
||||
* (%, _ and the escape char are escaped with the escape char)
|
||||
* For example, we will convert "foo/bar_baz%20cheese"
|
||||
* into "foo//bar/_baz/%20cheese" (if the escape char is '/').
|
||||
* @note consumers will have to use same escape char
|
||||
* when doing statements such as: ...LIKE '?1' ESCAPE '/'...
|
||||
* The number of entries in the array (each corresponding to a column in the
|
||||
* database row)
|
||||
*/
|
||||
AString escapeStringForLIKE(in AString aValue, in wchar aEscapeChar);
|
||||
readonly attribute unsigned long numEntries;
|
||||
|
||||
/**
|
||||
* Indicate the data type of the current result row for the the given column.
|
||||
* SQLite will perform type conversion if you ask for a value as a different
|
||||
* type than it is stored as.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based column index.
|
||||
* @return The type of the value at the given column index; one of
|
||||
* VALUE_TYPE_NULL, VALUE_TYPE_INTEGER, VALUE_TYPE_FLOAT,
|
||||
* VALUE_TYPE_TEXT, VALUE_TYPE_BLOB.
|
||||
*/
|
||||
long getTypeOfIndex(in unsigned long aIndex);
|
||||
|
||||
/**
|
||||
* Retrieve the contents of a column from the current result row as an
|
||||
* integer.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based colummn index.
|
||||
* @return Column value interpreted as an integer per type conversion rules.
|
||||
* @{
|
||||
*/
|
||||
long getInt32(in unsigned long aIndex);
|
||||
long long getInt64(in unsigned long aIndex);
|
||||
/** @} */
|
||||
/**
|
||||
* Retrieve the contents of a column from the current result row as a
|
||||
* floating point double.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based colummn index.
|
||||
* @return Column value interpreted as a double per type conversion rules.
|
||||
*/
|
||||
double getDouble(in unsigned long aIndex);
|
||||
/**
|
||||
* Retrieve the contents of a column from the current result row as a
|
||||
* string.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based colummn index.
|
||||
* @return The value for the result column interpreted as a string. If the
|
||||
* stored value was NULL, you will get an empty string with IsVoid set
|
||||
* to distinguish it from an explicitly set empty string.
|
||||
* @{
|
||||
*/
|
||||
AUTF8String getUTF8String(in unsigned long aIndex);
|
||||
AString getString(in unsigned long aIndex);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Retrieve the contents of a column from the current result row as a
|
||||
* blob.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based colummn index.
|
||||
* @param[out] aDataSize
|
||||
* The number of bytes in the blob.
|
||||
* @param[out] aData
|
||||
* The contents of the BLOB. This will be NULL if aDataSize == 0.
|
||||
*/
|
||||
void getBlob(in unsigned long aIndex, out unsigned long aDataSize, [array,size_is(aDataSize)] out octet aData);
|
||||
/**
|
||||
* Check whether the given column in the current result row is NULL.
|
||||
*
|
||||
* @param aIndex
|
||||
* 0-based colummn index.
|
||||
* @return true if the value for the result column is null.
|
||||
*/
|
||||
boolean getIsNull(in unsigned long aIndex);
|
||||
|
||||
/**
|
||||
* Returns a shared string pointer
|
||||
*/
|
||||
[noscript] void getSharedUTF8String(in unsigned long aIndex, out unsigned long aLength, [shared,retval] out string aResult);
|
||||
[noscript] void getSharedString(in unsigned long aIndex, out unsigned long aLength, [shared,retval] out wstring aResult);
|
||||
[noscript] void getSharedBlob(in unsigned long aIndex, out unsigned long aLength, [shared,retval] out octetPtr aResult);
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* Getters for native code that return their values as
|
||||
* the return type, for convenience and sanity.
|
||||
*
|
||||
* Not virtual; no vtable bloat.
|
||||
*/
|
||||
|
||||
inline PRInt32 AsInt32(PRUint32 idx) {
|
||||
PRInt32 v;
|
||||
GetInt32(idx, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline PRInt64 AsInt64(PRUint32 idx) {
|
||||
PRInt64 v;
|
||||
GetInt64(idx, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline double AsDouble(PRUint32 idx) {
|
||||
double v;
|
||||
GetDouble(idx, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline const char* AsSharedUTF8String(PRUint32 idx, PRUint32 *len) {
|
||||
const char *str = nsnull;
|
||||
GetSharedUTF8String(idx, len, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline const PRUnichar* AsSharedWString(PRUint32 idx, PRUint32 *len) {
|
||||
const PRUnichar *str = nsnull;
|
||||
GetSharedString(idx, len, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline const PRUint8* AsSharedBlob(PRUint32 idx, PRUint32 *len) {
|
||||
const PRUint8 *blob = nsnull;
|
||||
GetSharedBlob(idx, len, &blob);
|
||||
return blob;
|
||||
}
|
||||
|
||||
inline PRBool IsNull(PRUint32 idx) {
|
||||
PRBool b = PR_FALSE;
|
||||
GetIsNull(idx, &b);
|
||||
return b;
|
||||
}
|
||||
|
||||
%}
|
||||
};
|
||||
|
@ -41,8 +41,8 @@
|
||||
[ptr] native octetPtr(PRUint8);
|
||||
|
||||
/**
|
||||
* mozIStorageValueArray wraps an array of SQL values,
|
||||
* such as a single database row.
|
||||
* mozIStorageValueArray wraps an array of SQL values, such as a single database
|
||||
* row.
|
||||
*/
|
||||
[scriptable, uuid(07b5b93e-113c-4150-863c-d247b003a55d)]
|
||||
interface mozIStorageValueArray : nsISupports {
|
||||
|
83
storage/src/IStorageBindingParamsInternal.h
Normal file
83
storage/src/IStorageBindingParamsInternal.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_storage_IStorageBindingParamsInternal_h_
|
||||
#define mozilla_storage_IStorageBindingParamsInternal_h_
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
struct sqlite3_stmt;
|
||||
class mozIStorageError;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
#define ISTORAGEBINDINGPARAMSINTERNAL_IID \
|
||||
{0x4c43d33a, 0xc620, 0x41b8, {0xba, 0x1d, 0x50, 0xc5, 0xb1, 0xe9, 0x1a, 0x04}}
|
||||
|
||||
/**
|
||||
* Implementation-only interface for mozIStorageBindingParams. This defines the
|
||||
* set of methods required by the asynchronous execution code in order to
|
||||
* consume the contents stored in mozIStorageBindingParams instances.
|
||||
*/
|
||||
class IStorageBindingParamsInternal : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(ISTORAGEBINDINGPARAMSINTERNAL_IID)
|
||||
|
||||
/**
|
||||
* Binds our stored data to the statement.
|
||||
*
|
||||
* @param aStatement
|
||||
* The statement to bind our data to.
|
||||
* @return nsnull on success, or a mozIStorageError object if an error
|
||||
* occurred.
|
||||
*/
|
||||
virtual already_AddRefed<mozIStorageError> bind(sqlite3_stmt *aStatement) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(IStorageBindingParamsInternal,
|
||||
ISTORAGEBINDINGPARAMSINTERNAL_IID)
|
||||
|
||||
#define NS_DECL_ISTORAGEBINDINGPARAMSINTERNAL \
|
||||
already_AddRefed<mozIStorageError> bind(sqlite3_stmt *aStatement);
|
||||
|
||||
} // storage
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_storage_IStorageBindingParamsInternal_h_
|
@ -75,6 +75,10 @@ CPPSRCS = \
|
||||
mozStoragePrivateHelpers.cpp \
|
||||
mozStorageBindingParamsArray.cpp \
|
||||
mozStorageBindingParams.cpp \
|
||||
mozStorageAsyncStatement.cpp \
|
||||
mozStorageAsyncStatementJSHelper.cpp \
|
||||
mozStorageAsyncStatementParams.cpp \
|
||||
StorageBaseStatementInternal.cpp \
|
||||
SQLCollations.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
185
storage/src/StorageBaseStatementInternal.cpp
Normal file
185
storage/src/StorageBaseStatementInternal.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 "StorageBaseStatementInternal.h"
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozStorageStatementData.h"
|
||||
#include "mozStorageAsyncStatementExecution.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Local Classes
|
||||
|
||||
/**
|
||||
* Used to finalize an asynchronous statement on the background thread.
|
||||
*/
|
||||
class AsyncStatementFinalizer : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor for the event.
|
||||
*
|
||||
* @param aStatement
|
||||
* We need the AsyncStatement to be able to get at the sqlite3_stmt;
|
||||
* we only access/create it on the async thread.
|
||||
* @param aConnection
|
||||
* We need the connection to know what thread to release the statement
|
||||
* on. We release the statement on that thread since releasing the
|
||||
* statement might end up releasing the connection too.
|
||||
*/
|
||||
AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement,
|
||||
Connection *aConnection)
|
||||
: mStatement(aStatement)
|
||||
, mConnection(aConnection)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mStatement->internalAsyncFinalize();
|
||||
(void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
// It is vital that this remain an nsCOMPtr for NS_ProxyRelease's benefit.
|
||||
nsCOMPtr<StorageBaseStatementInternal> mStatement;
|
||||
nsRefPtr<Connection> mConnection;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// StorageBaseStatementInternal
|
||||
|
||||
StorageBaseStatementInternal::StorageBaseStatementInternal()
|
||||
: mAsyncStatement(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
StorageBaseStatementInternal::asyncFinalize()
|
||||
{
|
||||
nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
|
||||
if (!target) {
|
||||
// If we cannot get the background thread, we have to assume it has been
|
||||
// shutdown (or is in the process of doing so). As a result, we should
|
||||
// just finalize it here and now.
|
||||
internalAsyncFinalize();
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new AsyncStatementFinalizer(this, mDBConnection);
|
||||
|
||||
// If the dispatching did not go as planned, finalize now.
|
||||
if (!event ||
|
||||
NS_FAILED(target->Dispatch(event, NS_DISPATCH_NORMAL))) {
|
||||
internalAsyncFinalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageBaseStatementInternal::internalAsyncFinalize()
|
||||
{
|
||||
if (mAsyncStatement) {
|
||||
(void)::sqlite3_finalize(mAsyncStatement);
|
||||
mAsyncStatement = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageBaseStatementInternal::NewBindingParamsArray(
|
||||
mozIStorageBindingParamsArray **_array
|
||||
)
|
||||
{
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
|
||||
NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
array.forget(_array);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageBaseStatementInternal::ExecuteAsync(
|
||||
mozIStorageStatementCallback *aCallback,
|
||||
mozIStoragePendingStatement **_stmt
|
||||
)
|
||||
{
|
||||
// We used to call Connection::ExecuteAsync but it takes a
|
||||
// mozIStorageBaseStatement signature because it is also a public API. Since
|
||||
// our 'this' has no static concept of mozIStorageBaseStatement and Connection
|
||||
// would just QI it back across to a StorageBaseStatementInternal and the
|
||||
// actual logic is very simple, we now roll our own.
|
||||
nsTArray<StatementData> stmts(1);
|
||||
StatementData data;
|
||||
nsresult rv = getAsynchronousStatementData(data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Dispatch to the background
|
||||
return AsyncExecuteStatements::execute(stmts, mDBConnection, aCallback,
|
||||
_stmt);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageBaseStatementInternal::EscapeStringForLIKE(
|
||||
const nsAString &aValue,
|
||||
const PRUnichar aEscapeChar,
|
||||
nsAString &_escapedString
|
||||
)
|
||||
{
|
||||
const PRUnichar MATCH_ALL('%');
|
||||
const PRUnichar MATCH_ONE('_');
|
||||
|
||||
_escapedString.Truncate(0);
|
||||
|
||||
for (PRUint32 i = 0; i < aValue.Length(); i++) {
|
||||
if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
|
||||
aValue[i] == MATCH_ONE) {
|
||||
_escapedString += aEscapeChar;
|
||||
}
|
||||
_escapedString += aValue[i];
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
359
storage/src/StorageBaseStatementInternal.h
Normal file
359
storage/src/StorageBaseStatementInternal.h
Normal file
@ -0,0 +1,359 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_storage_StorageBaseStatementInternal_h_
|
||||
#define mozilla_storage_StorageBaseStatementInternal_h_
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
struct sqlite3_stmt;
|
||||
class mozIStorageError;
|
||||
class mozIStorageBindingParamsArray;
|
||||
class mozIStorageBindingParams;
|
||||
class mozIStorageStatementCallback;
|
||||
class mozIStoragePendingStatement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
#define STORAGEBASESTATEMENTINTERNAL_IID \
|
||||
{0xd18856c9, 0xbf07, 0x4ae2, {0x94, 0x5b, 0x1a, 0xdd, 0x49, 0x19, 0x55, 0x2a}}
|
||||
|
||||
class Connection;
|
||||
struct StatementData;
|
||||
|
||||
class AsyncStatementFinalizer;
|
||||
|
||||
/**
|
||||
* Implementation-only interface and shared logix mix-in corresponding to
|
||||
* mozIStorageBaseStatement. Both Statement and AsyncStatement inherit from
|
||||
* this. The interface aspect makes them look the same to implementation innards
|
||||
* that aren't publicly accessible. The mix-in avoids code duplication in
|
||||
* common implementations of mozIStorageBaseStatement, albeit with some minor
|
||||
* performance/space overhead because we have to use defines to officially
|
||||
* implement the methods on Statement/AsyncStatement (and proxy to this base
|
||||
* class.)
|
||||
*/
|
||||
class StorageBaseStatementInternal : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(STORAGEBASESTATEMENTINTERNAL_IID)
|
||||
|
||||
/**
|
||||
* @return the connection that this statement belongs to.
|
||||
*/
|
||||
Connection *getOwner()
|
||||
{
|
||||
return mDBConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the asynchronous statement, creating it if required.
|
||||
*
|
||||
* This is for use by the asynchronous execution code for StatementData
|
||||
* created by AsyncStatements. Statement internally uses this method to
|
||||
* prepopulate StatementData with the sqlite3_stmt.
|
||||
*
|
||||
* @param[out] stmt
|
||||
* The sqlite3_stmt for asynchronous use.
|
||||
* @return The SQLite result code for creating the statement if created,
|
||||
* SQLITE_OK if creation was not required.
|
||||
*/
|
||||
virtual int getAsyncStatement(sqlite3_stmt **_stmt) = 0;
|
||||
|
||||
/**
|
||||
* Obtains the StatementData needed for asynchronous execution.
|
||||
*
|
||||
* This is for use by Connection to retrieve StatementData from statements
|
||||
* when executeAsync is invoked.
|
||||
*
|
||||
* @param[out] _data
|
||||
* A reference to a StatementData object that will be populated
|
||||
* upon successful execution of this method.
|
||||
* @return NS_OK if we were able to assemble the data, failure otherwise.
|
||||
*/
|
||||
virtual nsresult getAsynchronousStatementData(StatementData &_data) = 0;
|
||||
|
||||
/**
|
||||
* Construct a new BindingParams to be owned by the provided binding params
|
||||
* array. This method exists so that BindingParamsArray does not need
|
||||
* factory logic to determine what type of BindingParams to instantiate.
|
||||
*
|
||||
* @param aOwner
|
||||
* The binding params array to own the newly created binding params.
|
||||
* @return The new mozIStorageBindingParams instance appropriate to the
|
||||
* underlying statement type.
|
||||
*/
|
||||
virtual already_AddRefed<mozIStorageBindingParams> newBindingParams(
|
||||
mozIStorageBindingParamsArray *aOwner
|
||||
) = 0;
|
||||
|
||||
protected: // mix-in bits are protected
|
||||
StorageBaseStatementInternal();
|
||||
|
||||
nsRefPtr<Connection> mDBConnection;
|
||||
|
||||
/**
|
||||
* Our asynchronous statement.
|
||||
*
|
||||
* For Statement this is populated by the first invocation to
|
||||
* getAsyncStatement.
|
||||
*
|
||||
* For AsyncStatement, this is null at creation time and initialized by the
|
||||
* async thread when it calls getAsyncStatement the first time the statement
|
||||
* is executed. (Or in the event of badly formed SQL, every time.)
|
||||
*/
|
||||
sqlite3_stmt *mAsyncStatement;
|
||||
|
||||
/**
|
||||
* Initiate asynchronous finalization by dispatching an event to the
|
||||
* asynchronous thread to finalize mAsyncStatement. This acquires a reference
|
||||
* to this statement and proxies it back to the connection's owning thread
|
||||
* for release purposes.
|
||||
*
|
||||
* In the event the asynchronous thread is already gone or we otherwise fail
|
||||
* to dispatch an event to it we failover to invoking internalAsyncFinalize
|
||||
* directly. (That's what the asynchronous finalizer would have called.)
|
||||
*
|
||||
* @note You must not call this method from your destructor because its
|
||||
* operation assumes we are still alive. Call internalAsyncFinalize
|
||||
* directly in that case.
|
||||
*/
|
||||
void asyncFinalize();
|
||||
|
||||
/**
|
||||
* Cleanup the async sqlite3_stmt stored in mAsyncStatement if it exists.
|
||||
*
|
||||
* @note Call this from your destructor, call asyncFinalize otherwise.
|
||||
*/
|
||||
void internalAsyncFinalize();
|
||||
|
||||
NS_IMETHOD NewBindingParamsArray(mozIStorageBindingParamsArray **_array);
|
||||
NS_IMETHOD ExecuteAsync(mozIStorageStatementCallback *aCallback,
|
||||
mozIStoragePendingStatement **_stmt);
|
||||
NS_IMETHOD EscapeStringForLIKE(const nsAString &aValue,
|
||||
const PRUnichar aEscapeChar,
|
||||
nsAString &_escapedString);
|
||||
|
||||
// Needs access to internalAsyncFinalize
|
||||
friend class AsyncStatementFinalizer;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(StorageBaseStatementInternal,
|
||||
STORAGEBASESTATEMENTINTERNAL_IID)
|
||||
|
||||
#define NS_DECL_STORAGEBASESTATEMENTINTERNAL \
|
||||
virtual Connection *getOwner(); \
|
||||
virtual int getAsyncStatement(sqlite3_stmt **_stmt); \
|
||||
virtual nsresult getAsynchronousStatementData(StatementData &_data); \
|
||||
virtual already_AddRefed<mozIStorageBindingParams> newBindingParams( \
|
||||
mozIStorageBindingParamsArray *aOwner);
|
||||
|
||||
/**
|
||||
* Helper macro to implement the proxying implementations. Because we are
|
||||
* implementing methods that are part of mozIStorageBaseStatement and the
|
||||
* implementation classes already use NS_DECL_MOZISTORAGEBASESTATEMENT we don't
|
||||
* need to provide declaration support.
|
||||
*/
|
||||
#define MIX_IMPL(_class, _optionalGuard, _method, _declArgs, _invokeArgs) \
|
||||
NS_IMETHODIMP _class::_method _declArgs \
|
||||
{ \
|
||||
_optionalGuard \
|
||||
return StorageBaseStatementInternal::_method _invokeArgs; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define proxying implementation for the given _class. If a state invariant
|
||||
* needs to be checked and an early return possibly performed, pass the clause
|
||||
* to use as _optionalGuard.
|
||||
*/
|
||||
#define MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(_class, _optionalGuard) \
|
||||
MIX_IMPL(_class, _optionalGuard, \
|
||||
NewBindingParamsArray, \
|
||||
(mozIStorageBindingParamsArray **_array), \
|
||||
(_array)) \
|
||||
MIX_IMPL(_class, _optionalGuard, \
|
||||
ExecuteAsync, \
|
||||
(mozIStorageStatementCallback *aCallback, \
|
||||
mozIStoragePendingStatement **_stmt), \
|
||||
(aCallback, _stmt)) \
|
||||
MIX_IMPL(_class, _optionalGuard, \
|
||||
EscapeStringForLIKE, \
|
||||
(const nsAString &aValue, const PRUnichar aEscapeChar, \
|
||||
nsAString &_escapedString), \
|
||||
(aValue, aEscapeChar, _escapedString))
|
||||
|
||||
/**
|
||||
* Name-building helper for BIND_GEN_IMPL.
|
||||
*/
|
||||
#define BIND_NAME_CONCAT(_nameBit, _concatBit) \
|
||||
Bind##_nameBit##_concatBit
|
||||
|
||||
/**
|
||||
* We have type-specific convenience methods for C++ implementations in
|
||||
* 3 different forms; 2 by index, 1 by name. The following macro allows
|
||||
* us to avoid having to define repetitive things by hand.
|
||||
*
|
||||
* Because of limitations of macros and our desire to avoid requiring special
|
||||
* permutations for the null and blob cases (whose argument count varies),
|
||||
* we require that the argument declarations and corresponding invocation
|
||||
* usages are passed in.
|
||||
*
|
||||
* @param _class
|
||||
* The class name.
|
||||
* @param _guard
|
||||
* The guard clause to inject.
|
||||
* @param _declName
|
||||
* The argument list (with parens) for the ByName variants.
|
||||
* @param _declIndex
|
||||
* The argument list (with parens) for the index variants.
|
||||
* @param _invArgs
|
||||
* The invocation argumment list.
|
||||
*/
|
||||
#define BIND_GEN_IMPL(_class, _guard, _name, _declName, _declIndex, _invArgs) \
|
||||
NS_IMETHODIMP _class::BIND_NAME_CONCAT(_name, ByName) _declName \
|
||||
{ \
|
||||
_guard \
|
||||
mozIStorageBindingParams *params = getParams(); \
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
||||
return params->BIND_NAME_CONCAT(_name, ByName) _invArgs; \
|
||||
} \
|
||||
NS_IMETHODIMP _class::BIND_NAME_CONCAT(_name, ByIndex) _declIndex \
|
||||
{ \
|
||||
_guard \
|
||||
mozIStorageBindingParams *params = getParams(); \
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
||||
return params->BIND_NAME_CONCAT(_name, ByIndex) _invArgs; \
|
||||
} \
|
||||
NS_IMETHODIMP _class::BIND_NAME_CONCAT(_name, Parameter) _declIndex \
|
||||
{ \
|
||||
_guard \
|
||||
mozIStorageBindingParams *params = getParams(); \
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
||||
return params->BIND_NAME_CONCAT(_name, ByIndex) _invArgs; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement BindByName/BindByIndex for the given class.
|
||||
*
|
||||
* @param _class The class name.
|
||||
* @param _optionalGuard The guard clause to inject.
|
||||
*/
|
||||
#define BIND_BASE_IMPLS(_class, _optionalGuard) \
|
||||
NS_IMETHODIMP _class::BindByName(const nsACString &aName, \
|
||||
nsIVariant *aValue) \
|
||||
{ \
|
||||
_optionalGuard \
|
||||
mozIStorageBindingParams *params = getParams(); \
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
||||
return params->BindByName(aName, aValue); \
|
||||
} \
|
||||
NS_IMETHODIMP _class::BindByIndex(PRUint32 aIndex, \
|
||||
nsIVariant *aValue) \
|
||||
{ \
|
||||
_optionalGuard \
|
||||
mozIStorageBindingParams *params = getParams(); \
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
||||
return params->BindByIndex(aIndex, aValue); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the various Bind*Parameter, Bind*ByIndex, Bind*ByName stubs that just
|
||||
* end up proxying to the params object.
|
||||
*/
|
||||
#define BOILERPLATE_BIND_PROXIES(_class, _optionalGuard) \
|
||||
BIND_BASE_IMPLS(_class, _optionalGuard) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
UTF8String, \
|
||||
(const nsACString &aWhere, \
|
||||
const nsACString &aValue), \
|
||||
(PRUint32 aWhere, \
|
||||
const nsACString &aValue), \
|
||||
(aWhere, aValue)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
String, \
|
||||
(const nsACString &aWhere, \
|
||||
const nsAString &aValue), \
|
||||
(PRUint32 aWhere, \
|
||||
const nsAString &aValue), \
|
||||
(aWhere, aValue)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
Double, \
|
||||
(const nsACString &aWhere, \
|
||||
double aValue), \
|
||||
(PRUint32 aWhere, \
|
||||
double aValue), \
|
||||
(aWhere, aValue)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
Int32, \
|
||||
(const nsACString &aWhere, \
|
||||
PRInt32 aValue), \
|
||||
(PRUint32 aWhere, \
|
||||
PRInt32 aValue), \
|
||||
(aWhere, aValue)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
Int64, \
|
||||
(const nsACString &aWhere, \
|
||||
PRInt64 aValue), \
|
||||
(PRUint32 aWhere, \
|
||||
PRInt64 aValue), \
|
||||
(aWhere, aValue)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
Null, \
|
||||
(const nsACString &aWhere), \
|
||||
(PRUint32 aWhere), \
|
||||
(aWhere)) \
|
||||
BIND_GEN_IMPL(_class, _optionalGuard, \
|
||||
Blob, \
|
||||
(const nsACString &aWhere, \
|
||||
const PRUint8 *aValue, \
|
||||
PRUint32 aValueSize), \
|
||||
(PRUint32 aWhere, \
|
||||
const PRUint8 *aValue, \
|
||||
PRUint32 aValueSize), \
|
||||
(aWhere, aValue, aValueSize))
|
||||
|
||||
|
||||
|
||||
} // storage
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_storage_StorageBaseStatementInternal_h_
|
456
storage/src/mozStorageAsyncStatement.cpp
Normal file
456
storage/src/mozStorageAsyncStatement.cpp
Normal file
@ -0,0 +1,456 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 mozStorage.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
* Shawn Wilsher <me@shawnwilsher.com>
|
||||
* John Zhang <jzhang@aptana.com>
|
||||
*
|
||||
* 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 <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
#include "Variant.h"
|
||||
|
||||
#include "mozIStorageError.h"
|
||||
|
||||
#include "mozStorageBindingParams.h"
|
||||
#include "mozStorageConnection.h"
|
||||
#include "mozStorageAsyncStatementJSHelper.h"
|
||||
#include "mozStorageAsyncStatementParams.h"
|
||||
#include "mozStoragePrivateHelpers.h"
|
||||
#include "mozStorageStatementRow.h"
|
||||
#include "mozStorageStatement.h"
|
||||
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo *gStorageLog;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIClassInfo
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER4(
|
||||
AsyncStatement
|
||||
, mozIStorageAsyncStatement
|
||||
, mozIStorageBaseStatement
|
||||
, mozIStorageBindingParams
|
||||
, mozilla::storage::StorageBaseStatementInternal
|
||||
)
|
||||
|
||||
class AsyncStatementClassInfo : public nsIClassInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetInterfaces(PRUint32 *_count, nsIID ***_array)
|
||||
{
|
||||
return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetHelperForLanguage(PRUint32 aLanguage, nsISupports **_helper)
|
||||
{
|
||||
if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
|
||||
static AsyncStatementJSHelper sJSHelper;
|
||||
*_helper = &sJSHelper;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_helper = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetContractID(char **_contractID)
|
||||
{
|
||||
*_contractID = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetClassDescription(char **_desc)
|
||||
{
|
||||
*_desc = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetClassID(nsCID **_id)
|
||||
{
|
||||
*_id = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetImplementationLanguage(PRUint32 *_language)
|
||||
{
|
||||
*_language = nsIProgrammingLanguage::CPLUSPLUS;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetFlags(PRUint32 *_flags)
|
||||
{
|
||||
*_flags = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetClassIDNoAlloc(nsCID *_cid)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) AsyncStatementClassInfo::AddRef() { return 2; }
|
||||
NS_IMETHODIMP_(nsrefcnt) AsyncStatementClassInfo::Release() { return 1; }
|
||||
NS_IMPL_QUERY_INTERFACE1(AsyncStatementClassInfo, nsIClassInfo)
|
||||
|
||||
static AsyncStatementClassInfo sAsyncStatementClassInfo;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// AsyncStatement
|
||||
|
||||
AsyncStatement::AsyncStatement()
|
||||
: StorageBaseStatementInternal()
|
||||
, mFinalized(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncStatement::initialize(Connection *aDBConnection,
|
||||
const nsACString &aSQLStatement)
|
||||
{
|
||||
NS_ASSERTION(aDBConnection, "No database connection given!");
|
||||
NS_ASSERTION(aDBConnection->GetNativeConnection(),
|
||||
"We should never be called with a null sqlite3 database!");
|
||||
|
||||
mDBConnection = aDBConnection;
|
||||
mSQLString = aSQLStatement;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
|
||||
mSQLString.get()));
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// We want to try and test for LIKE and that consumers are using
|
||||
// escapeStringForLIKE instead of just trusting user input. The idea to
|
||||
// check to see if they are binding a parameter after like instead of just
|
||||
// using a string. We only do this in debug builds because it's expensive!
|
||||
const nsCaseInsensitiveCStringComparator c;
|
||||
nsACString::const_iterator start, end, e;
|
||||
aSQLStatement.BeginReading(start);
|
||||
aSQLStatement.EndReading(end);
|
||||
e = end;
|
||||
while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
|
||||
// We have a LIKE in here, so we perform our tests
|
||||
// FindInReadable moves the iterator, so we have to get a new one for
|
||||
// each test we perform.
|
||||
nsACString::const_iterator s1, s2, s3;
|
||||
s1 = s2 = s3 = start;
|
||||
|
||||
if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
|
||||
::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
|
||||
::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
|
||||
// At this point, we didn't find a LIKE statement followed by ?, :,
|
||||
// or @, all of which are valid characters for binding a parameter.
|
||||
// We will warn the consumer that they may not be safely using LIKE.
|
||||
NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
|
||||
"are using mozIStorageAsyncStatement::escapeStringForLIKE "
|
||||
"and that you are binding that result to the statement "
|
||||
"to prevent SQL injection attacks.");
|
||||
}
|
||||
|
||||
// resetting start and e
|
||||
start = e;
|
||||
e = end;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozIStorageBindingParams *
|
||||
AsyncStatement::getParams()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// If we do not have an array object yet, make it.
|
||||
if (!mParamsArray) {
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> array;
|
||||
rv = NewBindingParamsArray(getter_AddRefs(array));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
mParamsArray = static_cast<BindingParamsArray *>(array.get());
|
||||
}
|
||||
|
||||
// If there isn't already any rows added, we'll have to add one to use.
|
||||
if (mParamsArray->length() == 0) {
|
||||
nsRefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
|
||||
NS_ENSURE_TRUE(params, nsnull);
|
||||
|
||||
rv = mParamsArray->AddParams(params);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
// We have to unlock our params because AddParams locks them. This is safe
|
||||
// because no reference to the params object was, or ever will be given out.
|
||||
params->unlock(nsnull);
|
||||
|
||||
// We also want to lock our array at this point - we don't want anything to
|
||||
// be added to it.
|
||||
mParamsArray->lock();
|
||||
}
|
||||
|
||||
return *mParamsArray->begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are here then we know there are no pending async executions relying on
|
||||
* us (StatementData holds a reference to us; this also goes for our own
|
||||
* AsyncStatementFinalizer which proxies its release to the calling thread) and
|
||||
* so it is always safe to destroy our sqlite3_stmt if one exists. We can be
|
||||
* destroyed on the caller thread by garbage-collection/reference counting or on
|
||||
* the async thread by the last execution of a statement that already lost its
|
||||
* main-thread refs.
|
||||
*/
|
||||
AsyncStatement::~AsyncStatement()
|
||||
{
|
||||
internalAsyncFinalize();
|
||||
cleanupJSHelpers();
|
||||
|
||||
// If we are getting destroyed on the wrong thread, proxy the connection
|
||||
// release to the right thread. I'm not sure why we do this.
|
||||
PRBool onCallingThread = PR_FALSE;
|
||||
(void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
|
||||
if (!onCallingThread) {
|
||||
// NS_ProxyRelase only magic forgets for us if mDBConnection is an
|
||||
// nsCOMPtr. Which it is not; it's an nsRefPtr.
|
||||
Connection *forgottenConn = nsnull;
|
||||
mDBConnection.swap(forgottenConn);
|
||||
(void)::NS_ProxyRelease(mDBConnection->threadOpenedOn, forgottenConn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStatement::cleanupJSHelpers()
|
||||
{
|
||||
// We are considered dead at this point, so any wrappers for row or params
|
||||
// need to lose their reference to us.
|
||||
if (mStatementParamsHolder) {
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
|
||||
do_QueryInterface(mStatementParamsHolder);
|
||||
nsCOMPtr<mozIStorageStatementParams> iParams =
|
||||
do_QueryWrappedNative(wrapper);
|
||||
AsyncStatementParams *params =
|
||||
static_cast<AsyncStatementParams *>(iParams.get());
|
||||
params->mStatement = nsnull;
|
||||
mStatementParamsHolder = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(AsyncStatement)
|
||||
NS_IMPL_THREADSAFE_RELEASE(AsyncStatement)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(AsyncStatement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
|
||||
NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
|
||||
if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
|
||||
foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// StorageBaseStatementInternal
|
||||
|
||||
Connection *
|
||||
AsyncStatement::getOwner()
|
||||
{
|
||||
return mDBConnection;
|
||||
}
|
||||
|
||||
int
|
||||
AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Make sure we are never called on the connection's owning thread.
|
||||
PRBool onOpenedThread = PR_FALSE;
|
||||
(void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
|
||||
NS_ASSERTION(!onOpenedThread,
|
||||
"We should only be called on the async thread!");
|
||||
#endif
|
||||
|
||||
if (!mAsyncStatement) {
|
||||
int rc = ::sqlite3_prepare_v2(mDBConnection->GetNativeConnection(),
|
||||
mSQLString.get(), -1,
|
||||
&mAsyncStatement, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
||||
("Sqlite statement prepare error: %d '%s'", rc,
|
||||
::sqlite3_errmsg(mDBConnection->GetNativeConnection())));
|
||||
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
||||
("Statement was: '%s'", mSQLString.get()));
|
||||
#endif
|
||||
*_stmt = nsnull;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
|
||||
mSQLString.get(),
|
||||
mAsyncStatement));
|
||||
#endif
|
||||
}
|
||||
|
||||
*_stmt = mAsyncStatement;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncStatement::getAsynchronousStatementData(StatementData &_data)
|
||||
{
|
||||
if (mFinalized)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// Pass null for the sqlite3_stmt; it will be requested on demand from the
|
||||
// async thread.
|
||||
_data = StatementData(nsnull, bindingParamsArray(), this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageBindingParams>
|
||||
AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
|
||||
{
|
||||
if (mFinalized)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
|
||||
return params.forget();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageAsyncStatement
|
||||
|
||||
// (nothing is specific to mozIStorageAsyncStatement)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// StorageBaseStatementInternal
|
||||
|
||||
// proxy to StorageBaseStatementInternal using its define helper.
|
||||
MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
|
||||
AsyncStatement,
|
||||
if (mFinalized) return NS_ERROR_UNEXPECTED;)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatement::Finalize()
|
||||
{
|
||||
if (mFinalized)
|
||||
return NS_OK;
|
||||
|
||||
mFinalized = true;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
|
||||
mSQLString.get()));
|
||||
#endif
|
||||
|
||||
asyncFinalize();
|
||||
cleanupJSHelpers();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
|
||||
{
|
||||
if (mFinalized)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
|
||||
if (array->getOwner() != this)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
if (array->length() == 0)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mParamsArray = array;
|
||||
mParamsArray->lock();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatement::GetState(PRInt32 *_state)
|
||||
{
|
||||
if (mFinalized)
|
||||
*_state = MOZ_STORAGE_STATEMENT_INVALID;
|
||||
else
|
||||
*_state = MOZ_STORAGE_STATEMENT_READY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageBindingParams
|
||||
|
||||
BOILERPLATE_BIND_PROXIES(
|
||||
AsyncStatement,
|
||||
if (mFinalized) return NS_ERROR_UNEXPECTED;
|
||||
)
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
145
storage/src/mozStorageAsyncStatement.h
Normal file
145
storage/src/mozStorageAsyncStatement.h
Normal file
@ -0,0 +1,145 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 Oracle Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Oracle Corporation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_storage_mozStorageAsyncStatement_h_
|
||||
#define mozilla_storage_mozStorageAsyncStatement_h_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozStorageStatementData.h"
|
||||
#include "mozIStorageAsyncStatement.h"
|
||||
#include "StorageBaseStatementInternal.h"
|
||||
|
||||
class nsIXPConnectJSObjectHolder;
|
||||
struct sqlite3_stmt;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class AsyncStatementJSHelper;
|
||||
class Connection;
|
||||
|
||||
class AsyncStatement : public mozIStorageAsyncStatement
|
||||
, public StorageBaseStatementInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEASYNCSTATEMENT
|
||||
NS_DECL_MOZISTORAGEBASESTATEMENT
|
||||
NS_DECL_MOZISTORAGEBINDINGPARAMS
|
||||
NS_DECL_STORAGEBASESTATEMENTINTERNAL
|
||||
|
||||
AsyncStatement();
|
||||
|
||||
/**
|
||||
* Initializes the object on aDBConnection by preparing the SQL statement
|
||||
* given by aSQLStatement.
|
||||
*
|
||||
* @param aDBConnection
|
||||
* The Connection object this statement is associated with.
|
||||
* @param aSQLStatement
|
||||
* The SQL statement to prepare that this object will represent.
|
||||
*/
|
||||
nsresult initialize(Connection *aDBConnection,
|
||||
const nsACString &aSQLStatement);
|
||||
|
||||
/**
|
||||
* Obtains and transfers ownership of the array of parameters that are bound
|
||||
* to this statment. This can be null.
|
||||
*/
|
||||
inline already_AddRefed<BindingParamsArray> bindingParamsArray()
|
||||
{
|
||||
return mParamsArray.forget();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
~AsyncStatement();
|
||||
|
||||
/**
|
||||
* Clean up the references JS helpers hold to us. For cycle-avoidance reasons
|
||||
* they do not hold reference-counted references to us, so it is important
|
||||
* we do this.
|
||||
*/
|
||||
void cleanupJSHelpers();
|
||||
|
||||
/**
|
||||
* @return a pointer to the BindingParams object to use with our Bind*
|
||||
* method.
|
||||
*/
|
||||
mozIStorageBindingParams *getParams();
|
||||
|
||||
/**
|
||||
* The SQL string as passed by the user. We store it because we create the
|
||||
* async statement on-demand on the async thread.
|
||||
*/
|
||||
nsCString mSQLString;
|
||||
|
||||
/**
|
||||
* Holds the array of parameters to bind to this statement when we execute
|
||||
* it asynchronously.
|
||||
*/
|
||||
nsRefPtr<BindingParamsArray> mParamsArray;
|
||||
|
||||
/**
|
||||
* Caches the JS 'params' helper for this statement.
|
||||
*/
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mStatementParamsHolder;
|
||||
|
||||
/**
|
||||
* Have we been explicitly finalized by the user?
|
||||
*/
|
||||
bool mFinalized;
|
||||
|
||||
/**
|
||||
* Required for access to private mStatementParamsHolder field by
|
||||
* AsyncStatementJSHelper::getParams.
|
||||
*/
|
||||
friend class AsyncStatementJSHelper;
|
||||
};
|
||||
|
||||
} // storage
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_storage_mozStorageAsyncStatement_h_
|
@ -138,7 +138,9 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the calling thread that the statement has finished executing.
|
||||
* Notifies the calling thread that the statement has finished executing. Keeps
|
||||
* the AsyncExecuteStatements instance alive long enough so that it does not
|
||||
* get destroyed on the async thread if there are no other references alive.
|
||||
*/
|
||||
class CompletionNotifier : public nsRunnable
|
||||
{
|
||||
@ -148,21 +150,26 @@ public:
|
||||
* dispatched to (which should always be the calling thread).
|
||||
*/
|
||||
CompletionNotifier(mozIStorageStatementCallback *aCallback,
|
||||
ExecutionState aReason) :
|
||||
mCallback(aCallback)
|
||||
ExecutionState aReason,
|
||||
AsyncExecuteStatements *aKeepAsyncAlive)
|
||||
: mKeepAsyncAlive(aKeepAsyncAlive)
|
||||
, mCallback(aCallback)
|
||||
, mReason(aReason)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
(void)mCallback->HandleCompletion(mReason);
|
||||
NS_RELEASE(mCallback);
|
||||
if (mCallback) {
|
||||
(void)mCallback->HandleCompletion(mReason);
|
||||
NS_RELEASE(mCallback);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncExecuteStatements> mKeepAsyncAlive;
|
||||
mozIStorageStatementCallback *mCallback;
|
||||
ExecutionState mReason;
|
||||
};
|
||||
@ -185,7 +192,7 @@ AsyncExecuteStatements::execute(StatementDataArray &aStatements,
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Dispatch it to the background
|
||||
nsCOMPtr<nsIEventTarget> target(aConnection->getAsyncExecutionTarget());
|
||||
nsIEventTarget *target = aConnection->getAsyncExecutionTarget();
|
||||
NS_ENSURE_TRUE(target, NS_ERROR_NOT_AVAILABLE);
|
||||
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -207,6 +214,7 @@ AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
|
||||
, mState(PENDING)
|
||||
, mCancelRequested(false)
|
||||
, mMutex(aConnection->sharedAsyncExecutionMutex)
|
||||
, mDBMutex(aConnection->sharedDBMutex)
|
||||
{
|
||||
(void)mStatements.SwapElements(aStatements);
|
||||
NS_ASSERTION(mStatements.Length(), "We weren't given any statements!");
|
||||
@ -236,7 +244,10 @@ AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
|
||||
{
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
|
||||
sqlite3_stmt *stmt(aData);
|
||||
sqlite3_stmt *aStatement = nsnull;
|
||||
// This cannot fail; we are only called if it's available.
|
||||
(void)aData.getSqliteStatement(&aStatement);
|
||||
NS_ASSERTION(aStatement, "You broke the code; do not call here like that!");
|
||||
BindingParamsArray *paramsArray(aData);
|
||||
|
||||
// Iterate through all of our parameters, bind them, and execute.
|
||||
@ -245,8 +256,9 @@ AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
|
||||
BindingParamsArray::iterator end = paramsArray->end();
|
||||
while (itr != end && continueProcessing) {
|
||||
// Bind the data to our statement.
|
||||
nsCOMPtr<mozIStorageError> error;
|
||||
error = (*itr)->bind(stmt);
|
||||
nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
|
||||
do_QueryInterface(*itr);
|
||||
nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement);
|
||||
if (error) {
|
||||
// Set our error state.
|
||||
mState = ERROR;
|
||||
@ -259,10 +271,10 @@ AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
|
||||
// Advance our iterator, execute, and then process the statement.
|
||||
itr++;
|
||||
bool lastStatement = aLastStatement && itr == end;
|
||||
continueProcessing = executeAndProcessStatement(stmt, lastStatement);
|
||||
continueProcessing = executeAndProcessStatement(aStatement, lastStatement);
|
||||
|
||||
// Always reset our statement.
|
||||
(void)::sqlite3_reset(stmt);
|
||||
(void)::sqlite3_reset(aStatement);
|
||||
}
|
||||
|
||||
return continueProcessing;
|
||||
@ -327,6 +339,9 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
|
||||
while (true) {
|
||||
// lock the sqlite mutex so sqlite3_errmsg cannot change
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
|
||||
int rc = ::sqlite3_step(aStatement);
|
||||
// Stop if we have no more results.
|
||||
if (rc == SQLITE_DONE)
|
||||
@ -338,6 +353,9 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
|
||||
|
||||
// Some errors are not fatal, and we can handle them and continue.
|
||||
if (rc == SQLITE_BUSY) {
|
||||
// Don't hold the lock while we call outside our module.
|
||||
SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
|
||||
|
||||
// Yield, and try again
|
||||
(void)::PR_Sleep(PR_INTERVAL_NO_WAIT);
|
||||
continue;
|
||||
@ -346,9 +364,13 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
|
||||
// Set an error state.
|
||||
mState = ERROR;
|
||||
|
||||
// And notify.
|
||||
sqlite3 *db = ::sqlite3_db_handle(aStatement);
|
||||
(void)notifyError(rc, ::sqlite3_errmsg(db));
|
||||
// Construct the error message before giving up the mutex (which we cannot
|
||||
// hold during the call to notifyError).
|
||||
sqlite3 *db = mConnection->GetNativeConnection();
|
||||
nsCOMPtr<mozIStorageError> errorObj(new Error(rc, ::sqlite3_errmsg(db)));
|
||||
// We cannot hold the DB mutex while calling notifyError.
|
||||
SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
|
||||
(void)notifyError(errorObj);
|
||||
|
||||
// Finally, indicate that we should stop processing.
|
||||
return false;
|
||||
@ -423,17 +445,16 @@ AsyncExecuteStatements::notifyComplete()
|
||||
mTransactionManager = nsnull;
|
||||
}
|
||||
|
||||
// Notify about completion iff we have a callback.
|
||||
if (mCallback) {
|
||||
nsRefPtr<CompletionNotifier> completionEvent =
|
||||
new CompletionNotifier(mCallback, mState);
|
||||
NS_ENSURE_TRUE(completionEvent, NS_ERROR_OUT_OF_MEMORY);
|
||||
// Always generate a completion notification; it is what guarantees that our
|
||||
// destruction does not happen here on the async thread.
|
||||
nsRefPtr<CompletionNotifier> completionEvent =
|
||||
new CompletionNotifier(mCallback, mState, this);
|
||||
NS_ENSURE_TRUE(completionEvent, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// We no longer own mCallback (the CompletionNotifier takes ownership).
|
||||
mCallback = nsnull;
|
||||
// We no longer own mCallback (the CompletionNotifier takes ownership).
|
||||
mCallback = nsnull;
|
||||
|
||||
(void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
(void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -443,6 +464,7 @@ AsyncExecuteStatements::notifyError(PRInt32 aErrorCode,
|
||||
const char *aMessage)
|
||||
{
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
mDBMutex.assertNotCurrentThreadOwns();
|
||||
|
||||
if (!mCallback)
|
||||
return NS_OK;
|
||||
@ -457,6 +479,7 @@ nsresult
|
||||
AsyncExecuteStatements::notifyError(mozIStorageError *aError)
|
||||
{
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
mDBMutex.assertNotCurrentThreadOwns();
|
||||
|
||||
if (!mCallback)
|
||||
return NS_OK;
|
||||
@ -546,13 +569,36 @@ AsyncExecuteStatements::Run()
|
||||
for (PRUint32 i = 0; i < mStatements.Length(); i++) {
|
||||
bool finished = (i == (mStatements.Length() - 1));
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
{ // lock the sqlite mutex so sqlite3_errmsg cannot change
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
|
||||
int rc = mStatements[i].getSqliteStatement(&stmt);
|
||||
if (rc != SQLITE_OK) {
|
||||
// Set our error state.
|
||||
mState = ERROR;
|
||||
|
||||
// Build the error object; can't call notifyError with the lock held
|
||||
sqlite3 *db = mConnection->GetNativeConnection();
|
||||
nsCOMPtr<mozIStorageError> errorObj(
|
||||
new Error(rc, ::sqlite3_errmsg(db))
|
||||
);
|
||||
{
|
||||
// We cannot hold the DB mutex and call notifyError.
|
||||
SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
|
||||
(void)notifyError(errorObj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have parameters to bind, bind them, execute, and process.
|
||||
if (mStatements[i].hasParametersToBeBound()) {
|
||||
if (!bindExecuteAndProcessStatement(mStatements[i], finished))
|
||||
break;
|
||||
}
|
||||
// Otherwise, just execute and process the statement.
|
||||
else if (!executeAndProcessStatement(mStatements[i], finished)) {
|
||||
else if (!executeAndProcessStatement(stmt, finished)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "SQLiteMutex.h"
|
||||
#include "mozIStoragePendingStatement.h"
|
||||
#include "mozIStorageStatementCallback.h"
|
||||
|
||||
@ -181,6 +182,7 @@ private:
|
||||
* Notifies callback about an error.
|
||||
*
|
||||
* @pre mMutex is not held
|
||||
* @pre mDBMutex is not held
|
||||
*
|
||||
* @param aErrorCode
|
||||
* The error code defined in mozIStorageError for the error.
|
||||
@ -235,6 +237,14 @@ private:
|
||||
* but not on the calling thread (see shouldNotify for why).
|
||||
*/
|
||||
Mutex &mMutex;
|
||||
|
||||
/**
|
||||
* The wrapped SQLite recursive connection mutex. We use it whenever we call
|
||||
* sqlite3_step and care about having reliable error messages. By taking it
|
||||
* prior to the call and holding it until the point where we no longer care
|
||||
* about the error message, the user gets reliable error messages.
|
||||
*/
|
||||
SQLiteMutex &mDBMutex;
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
|
147
storage/src/mozStorageAsyncStatementJSHelper.cpp
Normal file
147
storage/src/mozStorageAsyncStatementJSHelper.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 mozStorage code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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 "nsIXPConnect.h"
|
||||
#include "mozStorageAsyncStatement.h"
|
||||
#include "mozStorageService.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozStorageAsyncStatementJSHelper.h"
|
||||
|
||||
#include "mozStorageAsyncStatementParams.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// AsyncStatementJSHelper
|
||||
|
||||
nsresult
|
||||
AsyncStatementJSHelper::getParams(AsyncStatement *aStatement,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsval *_params)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
#ifdef DEBUG
|
||||
PRInt32 state;
|
||||
(void)aStatement->GetState(&state);
|
||||
NS_ASSERTION(state == mozIStorageAsyncStatement::MOZ_STORAGE_STATEMENT_READY,
|
||||
"Invalid state to get the params object - all calls will fail!");
|
||||
#endif
|
||||
|
||||
if (!aStatement->mStatementParamsHolder) {
|
||||
nsCOMPtr<mozIStorageStatementParams> params =
|
||||
new AsyncStatementParams(aStatement);
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsCOMPtr<nsIXPConnect> xpc(Service::getXPConnect());
|
||||
rv = xpc->WrapNative(
|
||||
aCtx,
|
||||
::JS_GetGlobalForObject(aCtx, aScopeObj),
|
||||
params,
|
||||
NS_GET_IID(mozIStorageStatementParams),
|
||||
getter_AddRefs(aStatement->mStatementParamsHolder)
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
JSObject *obj = nsnull;
|
||||
rv = aStatement->mStatementParamsHolder->GetJSObject(&obj);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*_params = OBJECT_TO_JSVAL(obj);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) AsyncStatementJSHelper::AddRef() { return 2; }
|
||||
NS_IMETHODIMP_(nsrefcnt) AsyncStatementJSHelper::Release() { return 1; }
|
||||
NS_INTERFACE_MAP_BEGIN(AsyncStatementJSHelper)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIXPCScriptable
|
||||
|
||||
#define XPC_MAP_CLASSNAME AsyncStatementJSHelper
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "AsyncStatementJSHelper"
|
||||
#define XPC_MAP_WANT_GETPROPERTY
|
||||
#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsval aId,
|
||||
jsval *_result,
|
||||
PRBool *_retval)
|
||||
{
|
||||
if (!JSVAL_IS_STRING(aId))
|
||||
return NS_OK;
|
||||
|
||||
// Cast to async via mozI* since direct from nsISupports is ambiguous.
|
||||
mozIStorageAsyncStatement *iAsyncStmt =
|
||||
static_cast<mozIStorageAsyncStatement *>(aWrapper->Native());
|
||||
AsyncStatement *stmt = static_cast<AsyncStatement *>(iAsyncStmt);
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsISupports *supp = aWrapper->Native();
|
||||
nsCOMPtr<mozIStorageAsyncStatement> isStatement(do_QueryInterface(supp));
|
||||
NS_ASSERTION(isStatement, "How is this not an async statement?!");
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *propName = ::JS_GetStringBytes(JSVAL_TO_STRING(aId));
|
||||
if (::strcmp(propName, "params") == 0)
|
||||
return getParams(stmt, aCtx, aScopeObj, _result);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
67
storage/src/mozStorageAsyncStatementJSHelper.h
Normal file
67
storage/src/mozStorageAsyncStatementJSHelper.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 mozStorage code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_storage_mozStorageAsyncStatementJSHelper_h_
|
||||
#define mozilla_storage_mozStorageAsyncStatementJSHelper_h_
|
||||
|
||||
#include "nsIXPCScriptable.h"
|
||||
|
||||
class AsyncStatement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
/**
|
||||
* A modified version of StatementJSHelper that only exposes the async-specific
|
||||
* 'params' helper. We do not expose 'row' or 'step' as they do not apply to
|
||||
* us.
|
||||
*/
|
||||
class AsyncStatementJSHelper : public nsIXPCScriptable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
|
||||
private:
|
||||
nsresult getParams(AsyncStatement *, JSContext *, JSObject *, jsval *);
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_storage_mozStorageAsyncStatementJSHelper_h_
|
165
storage/src/mozStorageAsyncStatementParams.cpp
Normal file
165
storage/src/mozStorageAsyncStatementParams.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 Oracle Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Oracle Corporation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
* Shawn Wilsher <me@shawnwilsher.com>
|
||||
*
|
||||
* 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 "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "mozStoragePrivateHelpers.h"
|
||||
#include "mozStorageAsyncStatement.h"
|
||||
#include "mozStorageAsyncStatementParams.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// AsyncStatementParams
|
||||
|
||||
AsyncStatementParams::AsyncStatementParams(AsyncStatement *aStatement)
|
||||
: mStatement(aStatement)
|
||||
{
|
||||
NS_ASSERTION(mStatement != nsnull, "mStatement is null");
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(
|
||||
AsyncStatementParams
|
||||
, mozIStorageStatementParams
|
||||
, nsIXPCScriptable
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIXPCScriptable
|
||||
|
||||
#define XPC_MAP_CLASSNAME AsyncStatementParams
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "AsyncStatementParams"
|
||||
#define XPC_MAP_WANT_SETPROPERTY
|
||||
#define XPC_MAP_WANT_NEWRESOLVE
|
||||
#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementParams::SetProperty(
|
||||
nsIXPConnectWrappedNative *aWrapper,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsval aId,
|
||||
jsval *_vp,
|
||||
PRBool *_retval
|
||||
)
|
||||
{
|
||||
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (JSVAL_IS_INT(aId)) {
|
||||
int idx = JSVAL_TO_INT(aId);
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByIndex(idx, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else if (JSVAL_IS_STRING(aId)) {
|
||||
JSString *str = JSVAL_TO_STRING(aId);
|
||||
NS_ConvertUTF16toUTF8 name(reinterpret_cast<const PRUnichar *>
|
||||
(::JS_GetStringChars(str)),
|
||||
::JS_GetStringLength(str));
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByName(name, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*_retval = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementParams::NewResolve(
|
||||
nsIXPConnectWrappedNative *aWrapper,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsval aId,
|
||||
PRUint32 aFlags,
|
||||
JSObject **_objp,
|
||||
PRBool *_retval
|
||||
)
|
||||
{
|
||||
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
|
||||
// We do not throw at any point after this unless our index is out of range
|
||||
// because we want to allow the prototype chain to be checked for the
|
||||
// property.
|
||||
|
||||
PRUint32 idx;
|
||||
|
||||
if (JSVAL_IS_INT(aId)) {
|
||||
idx = JSVAL_TO_INT(aId);
|
||||
// All indexes are good because we don't know how many parameters there
|
||||
// really are.
|
||||
}
|
||||
else if (JSVAL_IS_STRING(aId)) {
|
||||
JSString *str = JSVAL_TO_STRING(aId);
|
||||
jschar *nameChars = ::JS_GetStringChars(str);
|
||||
size_t nameLength = ::JS_GetStringLength(str);
|
||||
|
||||
// We are unable to tell if there's a parameter with this name and so
|
||||
// we must assume that there is. This screws the rest of the prototype
|
||||
// chain, but people really shouldn't be depending on this anyways.
|
||||
*_retval = ::JS_DefineUCProperty(aCtx, aScopeObj, nameChars, nameLength,
|
||||
JSVAL_VOID, nsnull, nsnull, 0);
|
||||
NS_ENSURE_TRUE(*_retval, NS_OK);
|
||||
}
|
||||
else {
|
||||
// We do not handle other types.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = ::JS_DefineElement(aCtx, aScopeObj, idx, JSVAL_VOID, nsnull,
|
||||
nsnull, 0);
|
||||
if (*_retval)
|
||||
*_objp = aScopeObj;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
77
storage/src/mozStorageAsyncStatementParams.h
Normal file
77
storage/src/mozStorageAsyncStatementParams.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 Oracle Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Oracle Corporation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_storage_mozStorageAsyncStatementParams_h_
|
||||
#define mozilla_storage_mozStorageAsyncStatementParams_h_
|
||||
|
||||
#include "mozIStorageStatementWrapper.h"
|
||||
#include "nsIXPCScriptable.h"
|
||||
|
||||
class mozIStorageAsyncStatement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class AsyncStatement;
|
||||
|
||||
/*
|
||||
* Since mozIStorageStatementParams is just a tagging interface we do not have
|
||||
* an async variant.
|
||||
*/
|
||||
class AsyncStatementParams : public mozIStorageStatementParams
|
||||
, public nsIXPCScriptable
|
||||
{
|
||||
public:
|
||||
AsyncStatementParams(AsyncStatement *aStatement);
|
||||
|
||||
// interfaces
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGESTATEMENTPARAMS
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
|
||||
protected:
|
||||
AsyncStatement *mStatement;
|
||||
|
||||
friend class AsyncStatement;
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_storage_mozStorageAsyncStatementParams_h_
|
@ -22,6 +22,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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
|
||||
@ -136,16 +137,32 @@ sqlite3_T_blob(BindingColumnData aData,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// BindingParams
|
||||
|
||||
BindingParams::BindingParams(BindingParamsArray *aOwningArray,
|
||||
BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray,
|
||||
Statement *aOwningStatement)
|
||||
: mOwningArray(aOwningArray)
|
||||
: mLocked(false)
|
||||
, mOwningArray(aOwningArray)
|
||||
, mOwningStatement(aOwningStatement)
|
||||
, mLocked(false)
|
||||
{
|
||||
(void)mOwningStatement->GetParameterCount(&mParamCount);
|
||||
(void)mParameters.SetCapacity(mParamCount);
|
||||
}
|
||||
|
||||
BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray)
|
||||
: mLocked(false)
|
||||
, mOwningArray(aOwningArray)
|
||||
, mOwningStatement(nsnull)
|
||||
, mParamCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
AsyncBindingParams::AsyncBindingParams(
|
||||
mozIStorageBindingParamsArray *aOwningArray
|
||||
)
|
||||
: BindingParams(aOwningArray)
|
||||
{
|
||||
mNamedParameters.Init();
|
||||
}
|
||||
|
||||
void
|
||||
BindingParams::lock()
|
||||
{
|
||||
@ -160,18 +177,75 @@ BindingParams::lock()
|
||||
}
|
||||
|
||||
void
|
||||
BindingParams::unlock()
|
||||
BindingParams::unlock(Statement *aOwningStatement)
|
||||
{
|
||||
NS_ASSERTION(mLocked == true, "Parameters were not yet locked!");
|
||||
mLocked = false;
|
||||
mOwningStatement = aOwningStatement;
|
||||
}
|
||||
|
||||
const BindingParamsArray *
|
||||
const mozIStorageBindingParamsArray *
|
||||
BindingParams::getOwner() const
|
||||
{
|
||||
return mOwningArray;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
AsyncBindingParams::iterateOverNamedParameters(const nsACString &aName,
|
||||
nsIVariant *aValue,
|
||||
void *voidClosureThunk)
|
||||
{
|
||||
NamedParameterIterationClosureThunk *closureThunk =
|
||||
static_cast<NamedParameterIterationClosureThunk *>(voidClosureThunk);
|
||||
|
||||
// We do not accept any forms of names other than ":name", but we need to add
|
||||
// the colon for SQLite.
|
||||
nsCAutoString name(":");
|
||||
name.Append(aName);
|
||||
int oneIdx = ::sqlite3_bind_parameter_index(closureThunk->statement,
|
||||
name.get());
|
||||
|
||||
if (oneIdx == 0) {
|
||||
nsCAutoString errMsg(aName);
|
||||
errMsg.Append(NS_LITERAL_CSTRING(" is not a valid named parameter."));
|
||||
closureThunk->err = new Error(SQLITE_RANGE, errMsg.get());
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
// XPCVariant's AddRef and Release are not thread-safe and so we must not do
|
||||
// anything that would invoke them here on the async thread. As such we can't
|
||||
// cram aValue into self->mParameters using ReplaceObjectAt so that we can
|
||||
// freeload off of the BindingParams::Bind implementation.
|
||||
int rc = variantToSQLiteT(BindingColumnData(closureThunk->statement,
|
||||
oneIdx - 1),
|
||||
aValue);
|
||||
if (rc != SQLITE_OK) {
|
||||
// We had an error while trying to bind. Now we need to create an error
|
||||
// object with the right message. Note that we special case
|
||||
// SQLITE_MISMATCH, but otherwise get the message from SQLite.
|
||||
const char *msg = "Could not covert nsIVariant to SQLite type.";
|
||||
if (rc != SQLITE_MISMATCH)
|
||||
msg = ::sqlite3_errmsg(::sqlite3_db_handle(closureThunk->statement));
|
||||
|
||||
closureThunk->err = new Error(rc, msg);
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(
|
||||
BindingParams
|
||||
, mozIStorageBindingParams
|
||||
, IStorageBindingParamsInternal
|
||||
)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// IStorageBindingParamsInternal
|
||||
|
||||
already_AddRefed<mozIStorageError>
|
||||
BindingParams::bind(sqlite3_stmt *aStatement)
|
||||
{
|
||||
@ -191,14 +265,26 @@ BindingParams::bind(sqlite3_stmt *aStatement)
|
||||
}
|
||||
}
|
||||
|
||||
// No error occurred, so return null!
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(
|
||||
BindingParams,
|
||||
mozIStorageBindingParams
|
||||
)
|
||||
already_AddRefed<mozIStorageError>
|
||||
AsyncBindingParams::bind(sqlite3_stmt * aStatement)
|
||||
{
|
||||
// We should bind by index using the super-class if there is nothing in our
|
||||
// hashtable.
|
||||
if (!mNamedParameters.Count())
|
||||
return BindingParams::bind(aStatement);
|
||||
|
||||
// Enumerate over everyone in the map, propagating them into mParameters if
|
||||
// we can and creating an error immediately when we cannot.
|
||||
NamedParameterIterationClosureThunk closureThunk = {this, aStatement, nsnull};
|
||||
(void)mNamedParameters.EnumerateRead(iterateOverNamedParameters,
|
||||
(void *)&closureThunk);
|
||||
|
||||
return closureThunk.err.forget();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageBindingParams
|
||||
@ -217,6 +303,18 @@ BindingParams::BindByName(const nsACString &aName,
|
||||
return BindByIndex(index, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncBindingParams::BindByName(const nsACString &aName,
|
||||
nsIVariant *aValue)
|
||||
{
|
||||
NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (!mNamedParameters.Put(aName, aValue))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
BindingParams::BindUTF8StringByName(const nsACString &aName,
|
||||
const nsACString &aValue)
|
||||
@ -305,6 +403,20 @@ BindingParams::BindByIndex(PRUint32 aIndex,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncBindingParams::BindByIndex(PRUint32 aIndex,
|
||||
nsIVariant *aValue)
|
||||
{
|
||||
NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
|
||||
// In the asynchronous case we do not know how many parameters there are to
|
||||
// bind to, so we cannot check the validity of aIndex.
|
||||
|
||||
// Store the variant for later use.
|
||||
NS_ENSURE_TRUE(mParameters.ReplaceObjectAt(aValue, aIndex),
|
||||
NS_ERROR_OUT_OF_MEMORY);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BindingParams::BindUTF8StringByIndex(PRUint32 aIndex,
|
||||
const nsACString &aValue)
|
||||
|
@ -22,6 +22,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
|
||||
* Andrew Sutherland <asutherland@asutherland.org>
|
||||
*
|
||||
* 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,25 +41,27 @@
|
||||
#ifndef _mozStorageBindingParams_h_
|
||||
#define _mozStorageBindingParams_h_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIVariant.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozStorageStatement.h"
|
||||
#include "mozIStorageBindingParams.h"
|
||||
#include "mozStorageAsyncStatement.h"
|
||||
|
||||
class mozIStorageError;
|
||||
struct sqlite3_stmt;
|
||||
#include "mozIStorageBindingParams.h"
|
||||
#include "IStorageBindingParamsInternal.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class BindingParams : public mozIStorageBindingParams
|
||||
, public IStorageBindingParamsInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEBINDINGPARAMS
|
||||
NS_DECL_ISTORAGEBINDINGPARAMSINTERNAL
|
||||
|
||||
/**
|
||||
* Locks the parameters and prevents further modification to it (such as
|
||||
@ -68,33 +71,82 @@ public:
|
||||
|
||||
/**
|
||||
* Unlocks the parameters and allows modification to it again.
|
||||
*/
|
||||
void unlock();
|
||||
|
||||
/**
|
||||
* @returns the pointer to the owning BindingParamsArray.
|
||||
*/
|
||||
const BindingParamsArray *getOwner() const;
|
||||
|
||||
/**
|
||||
* Binds our stored data to the statement.
|
||||
*
|
||||
* @param aStatement
|
||||
* The statement to bind our data to.
|
||||
* @returns nsnull on success, or a mozIStorageError object if an error
|
||||
* occurred.
|
||||
* @param aOwningStatement
|
||||
* The statement that owns us. We cleared this when we were locked,
|
||||
* and our invariant requires us to have this, so you need to tell us
|
||||
* again.
|
||||
*/
|
||||
already_AddRefed<mozIStorageError> bind(sqlite3_stmt *aStatement);
|
||||
void unlock(Statement *aOwningStatement);
|
||||
|
||||
BindingParams(BindingParamsArray *aOwningArray,
|
||||
/**
|
||||
* @returns the pointer to the owning BindingParamsArray. Used by a
|
||||
* BindingParamsArray to verify that we belong to it when added.
|
||||
*/
|
||||
const mozIStorageBindingParamsArray *getOwner() const;
|
||||
|
||||
BindingParams(mozIStorageBindingParamsArray *aOwningArray,
|
||||
Statement *aOwningStatement);
|
||||
virtual ~BindingParams() {}
|
||||
|
||||
protected:
|
||||
BindingParams(mozIStorageBindingParamsArray *aOwningArray);
|
||||
nsCOMArray<nsIVariant> mParameters;
|
||||
bool mLocked;
|
||||
|
||||
private:
|
||||
nsRefPtr<BindingParamsArray> mOwningArray;
|
||||
|
||||
/**
|
||||
* Track the BindingParamsArray that created us until we are added to it.
|
||||
* (Once we are added we are locked and no one needs to look up our owner.)
|
||||
* Ref-counted since there is no invariant that guarantees it stays alive
|
||||
* otherwise. This keeps mOwningStatement alive for us too since the array
|
||||
* also holds a reference.
|
||||
*/
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> mOwningArray;
|
||||
/**
|
||||
* Used in the synchronous binding case to map parameter names to indices.
|
||||
* Not reference-counted because this is only non-null as long as mOwningArray
|
||||
* is non-null and mOwningArray also holds a statement reference.
|
||||
*/
|
||||
Statement *mOwningStatement;
|
||||
nsCOMArray<nsIVariant> mParameters;
|
||||
PRUint32 mParamCount;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds late resolution of named parameters so they don't get resolved until we
|
||||
* try and bind the parameters on the async thread. We also stop checking
|
||||
* parameter indices for being too big since we just just don't know how many
|
||||
* there are.
|
||||
*
|
||||
* We support *either* binding by name or binding by index. Trying to do both
|
||||
* results in only binding by name at sqlite3_stmt bind time.
|
||||
*/
|
||||
class AsyncBindingParams : public BindingParams
|
||||
{
|
||||
public:
|
||||
NS_SCRIPTABLE NS_IMETHOD BindByName(const nsACString & aName,
|
||||
nsIVariant *aValue);
|
||||
NS_SCRIPTABLE NS_IMETHOD BindByIndex(PRUint32 aIndex, nsIVariant *aValue);
|
||||
|
||||
virtual already_AddRefed<mozIStorageError> bind(sqlite3_stmt * aStatement);
|
||||
|
||||
AsyncBindingParams(mozIStorageBindingParamsArray *aOwningArray);
|
||||
virtual ~AsyncBindingParams() {}
|
||||
|
||||
private:
|
||||
nsInterfaceHashtable<nsCStringHashKey, nsIVariant> mNamedParameters;
|
||||
|
||||
struct NamedParameterIterationClosureThunk
|
||||
{
|
||||
AsyncBindingParams *self;
|
||||
sqlite3_stmt *statement;
|
||||
nsCOMPtr<mozIStorageError> err;
|
||||
};
|
||||
|
||||
static PLDHashOperator iterateOverNamedParameters(const nsACString &aName,
|
||||
nsIVariant *aValue,
|
||||
void *voidClosureThunk);
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozStorageBindingParams.h"
|
||||
#include "StorageBaseStatementInternal.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
@ -46,7 +47,9 @@ namespace storage {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// BindingParamsArray
|
||||
|
||||
BindingParamsArray::BindingParamsArray(Statement *aOwningStatement)
|
||||
BindingParamsArray::BindingParamsArray(
|
||||
StorageBaseStatementInternal *aOwningStatement
|
||||
)
|
||||
: mOwningStatement(aOwningStatement)
|
||||
, mLocked(false)
|
||||
{
|
||||
@ -63,7 +66,7 @@ BindingParamsArray::lock()
|
||||
mOwningStatement = nsnull;
|
||||
}
|
||||
|
||||
const Statement *
|
||||
const StorageBaseStatementInternal *
|
||||
BindingParamsArray::getOwner() const
|
||||
{
|
||||
return mOwningStatement;
|
||||
@ -82,9 +85,9 @@ BindingParamsArray::NewBindingParams(mozIStorageBindingParams **_params)
|
||||
{
|
||||
NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<mozIStorageBindingParams> params =
|
||||
new BindingParams(this, mOwningStatement);
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsCOMPtr<mozIStorageBindingParams> params(
|
||||
mOwningStatement->newBindingParams(this));
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED);
|
||||
|
||||
params.forget(_params);
|
||||
return NS_OK;
|
||||
|
@ -48,8 +48,7 @@
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class BindingParams;
|
||||
class Statement;
|
||||
class StorageBaseStatementInternal;
|
||||
|
||||
class BindingParamsArray : public mozIStorageBindingParamsArray
|
||||
{
|
||||
@ -57,7 +56,7 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEBINDINGPARAMSARRAY
|
||||
|
||||
BindingParamsArray(Statement *aOwningStatement);
|
||||
BindingParamsArray(StorageBaseStatementInternal *aOwningStatement);
|
||||
|
||||
typedef nsTArray_base::size_type size_type;
|
||||
|
||||
@ -70,7 +69,7 @@ public:
|
||||
/**
|
||||
* @return the pointer to the owning BindingParamsArray.
|
||||
*/
|
||||
const Statement *getOwner() const;
|
||||
const StorageBaseStatementInternal *getOwner() const;
|
||||
|
||||
/**
|
||||
* @return the number of elemets the array contains.
|
||||
@ -100,7 +99,7 @@ public:
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
BindingParams *operator*()
|
||||
mozIStorageBindingParams *operator*()
|
||||
{
|
||||
NS_ASSERTION(mIndex < mArray->length(),
|
||||
"Dereferenceing an invalid value!");
|
||||
@ -132,8 +131,8 @@ public:
|
||||
return iterator(this, length());
|
||||
}
|
||||
private:
|
||||
nsRefPtr<Statement> mOwningStatement;
|
||||
nsTArray< nsRefPtr<BindingParams> > mArray;
|
||||
nsCOMPtr<StorageBaseStatementInternal> mOwningStatement;
|
||||
nsTArray< nsCOMPtr<mozIStorageBindingParams> > mArray;
|
||||
bool mLocked;
|
||||
|
||||
friend class iterator;
|
||||
|
@ -62,9 +62,11 @@
|
||||
#include "mozStorageConnection.h"
|
||||
#include "mozStorageService.h"
|
||||
#include "mozStorageStatement.h"
|
||||
#include "mozStorageAsyncStatement.h"
|
||||
#include "mozStorageArgValueArray.h"
|
||||
#include "mozStoragePrivateHelpers.h"
|
||||
#include "mozStorageStatementData.h"
|
||||
#include "StorageBaseStatementInternal.h"
|
||||
#include "SQLCollations.h"
|
||||
|
||||
#include "prlog.h"
|
||||
@ -296,10 +298,10 @@ private:
|
||||
|
||||
Connection::Connection(Service *aService)
|
||||
: sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
|
||||
, sharedDBMutex("Connection::sharedDBMutex")
|
||||
, threadOpenedOn(do_GetCurrentThread())
|
||||
, mDBConn(nsnull)
|
||||
, mAsyncExecutionThreadShuttingDown(false)
|
||||
, mDBMutex("Connection::mDBMutex")
|
||||
, mTransactionInProgress(PR_FALSE)
|
||||
, mProgressHandler(nsnull)
|
||||
, mStorageService(aService)
|
||||
@ -317,7 +319,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(
|
||||
mozIStorageConnection
|
||||
)
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
nsIEventTarget *
|
||||
Connection::getAsyncExecutionTarget()
|
||||
{
|
||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||
@ -335,9 +337,7 @@ Connection::getAsyncExecutionTarget()
|
||||
}
|
||||
}
|
||||
|
||||
nsIEventTarget *eventTarget;
|
||||
NS_ADDREF(eventTarget = mAsyncExecutionThread);
|
||||
return eventTarget;
|
||||
return mAsyncExecutionThread;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -367,7 +367,7 @@ Connection::initialize(nsIFile *aDatabaseFile)
|
||||
}
|
||||
|
||||
// Properly wrap the database handle's mutex.
|
||||
mDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
|
||||
sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (!gStorageLog)
|
||||
@ -487,7 +487,7 @@ Connection::databaseElementExists(enum DatabaseElementType aElementType,
|
||||
bool
|
||||
Connection::findFunctionByInstance(nsISupports *aInstance)
|
||||
{
|
||||
mDBMutex.assertCurrentThreadOwns();
|
||||
sharedDBMutex.assertCurrentThreadOwns();
|
||||
FFEArguments args = { aInstance, false };
|
||||
mFunctions.EnumerateRead(findFunctionEnumerator, &args);
|
||||
return args.found;
|
||||
@ -503,7 +503,7 @@ Connection::sProgressHelper(void *aArg)
|
||||
int
|
||||
Connection::progressHandler()
|
||||
{
|
||||
mDBMutex.assertCurrentThreadOwns();
|
||||
sharedDBMutex.assertCurrentThreadOwns();
|
||||
if (mProgressHandler) {
|
||||
PRBool result;
|
||||
nsresult rv = mProgressHandler->OnProgress(this, &result);
|
||||
@ -610,7 +610,7 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
|
||||
if (!mDBConn)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> asyncThread(getAsyncExecutionTarget());
|
||||
nsIEventTarget *asyncThread = getAsyncExecutionTarget();
|
||||
NS_ENSURE_TRUE(asyncThread, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = setClosedState();
|
||||
@ -732,6 +732,25 @@ Connection::CreateStatement(const nsACString &aSQLStatement,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
|
||||
mozIStorageAsyncStatement **_stmt)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_stmt);
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsRefPtr<AsyncStatement> statement(new AsyncStatement());
|
||||
NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = statement->initialize(this, aSQLStatement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
AsyncStatement *rawPtr;
|
||||
statement.forget(&rawPtr);
|
||||
*_stmt = rawPtr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
|
||||
{
|
||||
@ -742,22 +761,23 @@ Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
|
||||
return convertResultCode(srv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Connection::ExecuteAsync(mozIStorageStatement **aStatements,
|
||||
NS_IMETHODIMP
|
||||
Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
|
||||
PRUint32 aNumStatements,
|
||||
mozIStorageStatementCallback *aCallback,
|
||||
mozIStoragePendingStatement **_handle)
|
||||
{
|
||||
nsTArray<StatementData> stmts(aNumStatements);
|
||||
for (PRUint32 i = 0; i < aNumStatements; i++) {
|
||||
Statement *stmt = static_cast<Statement *>(aStatements[i]);
|
||||
nsCOMPtr<StorageBaseStatementInternal> stmt =
|
||||
do_QueryInterface(aStatements[i]);
|
||||
|
||||
// Obtain our StatementData.
|
||||
StatementData data;
|
||||
nsresult rv = stmt->getAsynchronousStatementData(data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(::sqlite3_db_handle(stmt->nativeStatement()) == mDBConn,
|
||||
NS_ASSERTION(stmt->getOwner() == this,
|
||||
"Statement must be from this database connection!");
|
||||
|
||||
// Now append it to our array.
|
||||
@ -787,7 +807,7 @@ Connection::GetTransactionInProgress(PRBool *_inProgress)
|
||||
{
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
*_inProgress = mTransactionInProgress;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -803,7 +823,7 @@ Connection::BeginTransactionAs(PRInt32 aTransactionType)
|
||||
{
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
if (mTransactionInProgress)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsresult rv;
|
||||
@ -830,7 +850,7 @@ Connection::CommitTransaction()
|
||||
{
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
if (!mTransactionInProgress)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
|
||||
@ -844,7 +864,7 @@ Connection::RollbackTransaction()
|
||||
{
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
if (!mTransactionInProgress)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
|
||||
@ -878,7 +898,7 @@ Connection::CreateFunction(const nsACString &aFunctionName,
|
||||
|
||||
// Check to see if this function is already defined. We only check the name
|
||||
// because a function can be defined with the same body but different names.
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
|
||||
|
||||
int srv = ::sqlite3_create_function(mDBConn,
|
||||
@ -906,7 +926,7 @@ Connection::CreateAggregateFunction(const nsACString &aFunctionName,
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
// Check to see if this function name is already defined.
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
|
||||
|
||||
// Because aggregate functions depend on state across calls, you cannot have
|
||||
@ -936,7 +956,7 @@ Connection::RemoveFunction(const nsACString &aFunctionName)
|
||||
{
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
|
||||
|
||||
int srv = ::sqlite3_create_function(mDBConn,
|
||||
@ -963,7 +983,7 @@ Connection::SetProgressHandler(PRInt32 aGranularity,
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
// Return previous one
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
NS_IF_ADDREF(*_oldHandler = mProgressHandler);
|
||||
|
||||
if (!aHandler || aGranularity <= 0) {
|
||||
@ -982,7 +1002,7 @@ Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
|
||||
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
// Return previous one
|
||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
|
||||
NS_IF_ADDREF(*_oldHandler = mProgressHandler);
|
||||
|
||||
mProgressHandler = nsnull;
|
||||
|
@ -92,16 +92,24 @@ public:
|
||||
*
|
||||
* @returns an event target suitable for asynchronous statement execution.
|
||||
*/
|
||||
already_AddRefed<nsIEventTarget> getAsyncExecutionTarget();
|
||||
nsIEventTarget *getAsyncExecutionTarget();
|
||||
|
||||
/**
|
||||
* Mutex used by asynchronous statements to protect state. The mutex is
|
||||
* declared on the connection object because there is no contention between
|
||||
* asynchronous statements (they are serialized on mAsyncExecutionThread). It also
|
||||
* protects mPendingStatements.
|
||||
* asynchronous statements (they are serialized on mAsyncExecutionThread). It
|
||||
* also protects mPendingStatements.
|
||||
*/
|
||||
Mutex sharedAsyncExecutionMutex;
|
||||
|
||||
/**
|
||||
* Wraps the mutex that SQLite gives us from sqlite3_db_mutex. This is public
|
||||
* because we already expose the sqlite3* native connection and proper
|
||||
* operation of the deadlock detector requires everyone to use the same single
|
||||
* SQLiteMutex instance for correctness.
|
||||
*/
|
||||
SQLiteMutex sharedDBMutex;
|
||||
|
||||
/**
|
||||
* References the thread this database was opened on. This MUST be thread it is
|
||||
* closed on.
|
||||
@ -174,11 +182,6 @@ private:
|
||||
*/
|
||||
bool mAsyncExecutionThreadShuttingDown;
|
||||
|
||||
/**
|
||||
* Wraps the mutex that SQLite gives us from sqlite3_db_mutex.
|
||||
*/
|
||||
SQLiteMutex mDBMutex;
|
||||
|
||||
/**
|
||||
* Tracks if we have a transaction in progress or not. Access protected by
|
||||
* mDBMutex.
|
||||
|
@ -48,9 +48,11 @@
|
||||
#include "nsError.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "Variant.h"
|
||||
#include "mozStoragePrivateHelpers.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStorageCompletionCallback.h"
|
||||
#include "mozIStorageBindingParams.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
@ -122,23 +124,16 @@ checkAndLogStatementPerformance(sqlite3_stmt *aStatement)
|
||||
NS_WARNING(message.get());
|
||||
}
|
||||
|
||||
bool
|
||||
bindJSValue(JSContext *aCtx,
|
||||
mozIStorageStatement *aStatement,
|
||||
int aIdx,
|
||||
jsval aValue)
|
||||
nsIVariant *
|
||||
convertJSValToVariant(
|
||||
JSContext *aCtx,
|
||||
jsval aValue)
|
||||
{
|
||||
if (JSVAL_IS_INT(aValue)) {
|
||||
int v = JSVAL_TO_INT(aValue);
|
||||
(void)aStatement->BindInt32Parameter(aIdx, v);
|
||||
return true;
|
||||
}
|
||||
if (JSVAL_IS_INT(aValue))
|
||||
return new IntegerVariant(JSVAL_TO_INT(aValue));
|
||||
|
||||
if (JSVAL_IS_DOUBLE(aValue)) {
|
||||
double d = *JSVAL_TO_DOUBLE(aValue);
|
||||
(void)aStatement->BindDoubleParameter(aIdx, d);
|
||||
return true;
|
||||
}
|
||||
if (JSVAL_IS_DOUBLE(aValue))
|
||||
return new FloatVariant(*JSVAL_TO_DOUBLE(aValue));
|
||||
|
||||
if (JSVAL_IS_STRING(aValue)) {
|
||||
JSString *str = JSVAL_TO_STRING(aValue);
|
||||
@ -146,38 +141,33 @@ bindJSValue(JSContext *aCtx,
|
||||
reinterpret_cast<PRUnichar *>(::JS_GetStringChars(str)),
|
||||
::JS_GetStringLength(str)
|
||||
);
|
||||
(void)aStatement->BindStringParameter(aIdx, value);
|
||||
return true;
|
||||
return new TextVariant(value);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_BOOLEAN(aValue)) {
|
||||
(void)aStatement->BindInt32Parameter(aIdx, (aValue == JSVAL_TRUE) ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
if (JSVAL_IS_BOOLEAN(aValue))
|
||||
return new IntegerVariant((aValue == JSVAL_TRUE) ? 1 : 0);
|
||||
|
||||
if (JSVAL_IS_NULL(aValue)) {
|
||||
(void)aStatement->BindNullParameter(aIdx);
|
||||
return true;
|
||||
}
|
||||
if (JSVAL_IS_NULL(aValue))
|
||||
return new NullVariant();
|
||||
|
||||
if (JSVAL_IS_OBJECT(aValue)) {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(aValue);
|
||||
// some special things
|
||||
// We only support Date instances, all others fail.
|
||||
if (!::js_DateIsValid(aCtx, obj))
|
||||
return false;
|
||||
return nsnull;
|
||||
|
||||
double msecd = ::js_DateGetMsecSinceEpoch(aCtx, obj);
|
||||
msecd *= 1000.0;
|
||||
PRInt64 msec;
|
||||
LL_D2L(msec, msecd);
|
||||
|
||||
(void)aStatement->BindInt64Parameter(aIdx, msec);
|
||||
return true;
|
||||
return new IntegerVariant(msec);
|
||||
}
|
||||
|
||||
return false;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
class CallbackEvent : public nsRunnable
|
||||
{
|
||||
|
@ -52,7 +52,8 @@
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class mozIStorageCompletionCallback;
|
||||
class mozIStorageStatement;
|
||||
class mozIStorageBaseStatement;
|
||||
class mozIStorageBindingParams;
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
@ -88,20 +89,18 @@ nsresult convertResultCode(int aSQLiteResultCode);
|
||||
void checkAndLogStatementPerformance(sqlite3_stmt *aStatement);
|
||||
|
||||
/**
|
||||
* Binds a jsval to a statement at the given index.
|
||||
* Convert the provided jsval into a variant representation if possible.
|
||||
*
|
||||
* @param aCtx
|
||||
* The JSContext jsval is associated with.
|
||||
* @param aStatement
|
||||
* The statement to bind to.
|
||||
* @param aIdx
|
||||
* The one-based index to bind aValue to.
|
||||
* The JSContext the value is from.
|
||||
* @param aValue
|
||||
* The value to bind to aStatement.
|
||||
* @return true if we bound the value to the statement, false otherwise.
|
||||
* The JavaScript value to convert. All primitive types are supported,
|
||||
* but only Date objects are supported from the Date family. Date
|
||||
* objects are coerced to PRTime (nanoseconds since epoch) values.
|
||||
* @return the variant if conversion was successful, nsnull if conversion
|
||||
* failed. The caller is responsible for addref'ing if non-null.
|
||||
*/
|
||||
bool bindJSValue(JSContext *aCtx, mozIStorageStatement *aStatement, int aIdx,
|
||||
jsval aValue);
|
||||
nsIVariant *convertJSValToVariant(JSContext *aCtx, jsval aValue);
|
||||
|
||||
/**
|
||||
* Obtains an event that will notify a completion callback about completion.
|
||||
@ -110,8 +109,9 @@ bool bindJSValue(JSContext *aCtx, mozIStorageStatement *aStatement, int aIdx,
|
||||
* The callback to be notified.
|
||||
* @return an nsIRunnable that can be dispatched to the calling thread.
|
||||
*/
|
||||
already_AddRefed<nsIRunnable>
|
||||
newCompletionEvent(mozIStorageCompletionCallback *aCallback);
|
||||
already_AddRefed<nsIRunnable> newCompletionEvent(
|
||||
mozIStorageCompletionCallback *aCallback
|
||||
);
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
||||
|
@ -39,14 +39,15 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
#include "Variant.h"
|
||||
|
||||
#include "mozIStorageError.h"
|
||||
|
||||
@ -67,55 +68,16 @@ extern PRLogModuleInfo* gStorageLog;
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Local Classes
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Used to finalize an asynchronous statement on the background thread.
|
||||
*/
|
||||
class AsyncStatementFinalizer : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor for the event.
|
||||
*
|
||||
* @param aStatement
|
||||
* The sqlite3_stmt to finalize on the background thread.
|
||||
* @param aConnection
|
||||
* The Connection that aStatement was created on. We hold a reference
|
||||
* to this to ensure that if we are the last reference to the
|
||||
* Connection, that we release it on the proper thread. The release
|
||||
* call is proxied to the appropriate thread.
|
||||
*/
|
||||
AsyncStatementFinalizer(sqlite3_stmt *aStatement,
|
||||
Connection *aConnection)
|
||||
: mStatement(aStatement)
|
||||
, mConnection(aConnection)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
(void)::sqlite3_finalize(mStatement);
|
||||
(void)::NS_ProxyRelease(mConnection->threadOpenedOn, mConnection);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
sqlite3_stmt *mStatement;
|
||||
nsCOMPtr<Connection> mConnection;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIClassInfo
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER2(
|
||||
NS_IMPL_CI_INTERFACE_GETTER5(
|
||||
Statement,
|
||||
mozIStorageStatement,
|
||||
mozIStorageValueArray
|
||||
mozIStorageBaseStatement,
|
||||
mozIStorageBindingParams,
|
||||
mozIStorageValueArray,
|
||||
mozilla::storage::StorageBaseStatementInternal
|
||||
)
|
||||
|
||||
class StatementClassInfo : public nsIClassInfo
|
||||
@ -194,11 +156,10 @@ static StatementClassInfo sStatementClassInfo;
|
||||
//// Statement
|
||||
|
||||
Statement::Statement()
|
||||
: mDBConnection(nsnull)
|
||||
: StorageBaseStatementInternal()
|
||||
, mDBStatement(NULL)
|
||||
, mColumnNames()
|
||||
, mExecuting(false)
|
||||
, mCachedAsyncStatement(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -279,48 +240,7 @@ Statement::initialize(Connection *aDBConnection,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Statement::getAsynchronousStatementData(StatementData &_data)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = getAsyncStatement(&stmt);
|
||||
if (rc != SQLITE_OK)
|
||||
return convertResultCode(rc);
|
||||
|
||||
_data = StatementData(stmt, bindingParamsArray(), this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
Statement::getAsyncStatement(sqlite3_stmt **_stmt)
|
||||
{
|
||||
// If we have no statement, we shouldn't be calling this method!
|
||||
NS_ASSERTION(mDBStatement != NULL, "We have no statement to clone!");
|
||||
|
||||
// If we do not yet have a cached async statement, clone our statement now.
|
||||
if (!mCachedAsyncStatement) {
|
||||
int rc = ::sqlite3_prepare_v2(mDBConnection->GetNativeConnection(),
|
||||
::sqlite3_sql(mDBStatement), -1,
|
||||
&mCachedAsyncStatement, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
return rc;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE,
|
||||
("Cloned statement 0x%p to 0x%p", mDBStatement,
|
||||
mCachedAsyncStatement));
|
||||
#endif
|
||||
}
|
||||
|
||||
*_stmt = mCachedAsyncStatement;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
BindingParams *
|
||||
mozIStorageBindingParams *
|
||||
Statement::getParams()
|
||||
{
|
||||
nsresult rv;
|
||||
@ -344,7 +264,7 @@ Statement::getParams()
|
||||
|
||||
// We have to unlock our params because AddParams locks them. This is safe
|
||||
// because no reference to the params object was, or ever will be given out.
|
||||
params->unlock();
|
||||
params->unlock(this);
|
||||
|
||||
// We also want to lock our array at this point - we don't want anything to
|
||||
// be added to it. Nothing has, or will ever get a reference to it, but we
|
||||
@ -357,25 +277,94 @@ Statement::getParams()
|
||||
|
||||
Statement::~Statement()
|
||||
{
|
||||
(void)Finalize();
|
||||
(void)internalFinalize(true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(Statement)
|
||||
NS_IMPL_THREADSAFE_RELEASE(Statement)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(Statement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
|
||||
NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
|
||||
if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
|
||||
foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo);
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// StorageBaseStatementInternal
|
||||
|
||||
Connection *
|
||||
Statement::getOwner()
|
||||
{
|
||||
return mDBConnection;
|
||||
}
|
||||
|
||||
int
|
||||
Statement::getAsyncStatement(sqlite3_stmt **_stmt)
|
||||
{
|
||||
// If we have no statement, we shouldn't be calling this method!
|
||||
NS_ASSERTION(mDBStatement != NULL, "We have no statement to clone!");
|
||||
|
||||
// If we do not yet have a cached async statement, clone our statement now.
|
||||
if (!mAsyncStatement) {
|
||||
int rc = ::sqlite3_prepare_v2(mDBConnection->GetNativeConnection(),
|
||||
::sqlite3_sql(mDBStatement), -1,
|
||||
&mAsyncStatement, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
*_stmt = nsnull;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE,
|
||||
("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
|
||||
#endif
|
||||
}
|
||||
|
||||
*_stmt = mAsyncStatement;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Statement::getAsynchronousStatementData(StatementData &_data)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = getAsyncStatement(&stmt);
|
||||
if (rc != SQLITE_OK)
|
||||
return convertResultCode(rc);
|
||||
|
||||
_data = StatementData(stmt, bindingParamsArray(), this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageBindingParams>
|
||||
Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
|
||||
{
|
||||
nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
|
||||
return params.forget();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageStatement
|
||||
|
||||
// proxy to StorageBaseStatementInternal using its define helper.
|
||||
MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::Clone(mozIStorageStatement **_statement)
|
||||
{
|
||||
@ -392,6 +381,12 @@ Statement::Clone(mozIStorageStatement **_statement)
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::Finalize()
|
||||
{
|
||||
return internalFinalize(false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Statement::internalFinalize(bool aDestructing)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_OK;
|
||||
@ -404,26 +399,13 @@ Statement::Finalize()
|
||||
int srv = ::sqlite3_finalize(mDBStatement);
|
||||
mDBStatement = NULL;
|
||||
|
||||
// We need to finalize our async statement too, but want to make sure that any
|
||||
// queued up statements run first. Dispatch an event to the background thread
|
||||
// that will do this for us.
|
||||
if (mCachedAsyncStatement) {
|
||||
nsCOMPtr<nsIEventTarget> target = mDBConnection->getAsyncExecutionTarget();
|
||||
if (!target) {
|
||||
// However, if we cannot get the background thread, we have to assume it
|
||||
// has been shutdown (or is in the process of doing so). As a result, we
|
||||
// should just finalize it here and now.
|
||||
(void)::sqlite3_finalize(mCachedAsyncStatement);
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new AsyncStatementFinalizer(mCachedAsyncStatement, mDBConnection);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mCachedAsyncStatement = NULL;
|
||||
}
|
||||
if (mAsyncStatement) {
|
||||
// If the destructor called us, there are no pending async statements (they
|
||||
// hold a reference to us) and we can/must just kill the statement directly.
|
||||
if (aDestructing)
|
||||
internalAsyncFinalize();
|
||||
else
|
||||
asyncFinalize();
|
||||
}
|
||||
|
||||
// We are considered dead at this point, so any wrappers for row or params
|
||||
@ -570,97 +552,6 @@ Statement::Reset()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindUTF8StringParameter(PRUint32 aParamIndex,
|
||||
const nsACString &aValue)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindUTF8StringByIndex(aParamIndex, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindStringParameter(PRUint32 aParamIndex,
|
||||
const nsAString &aValue)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindStringByIndex(aParamIndex, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindDoubleParameter(PRUint32 aParamIndex,
|
||||
double aValue)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindDoubleByIndex(aParamIndex, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindInt32Parameter(PRUint32 aParamIndex,
|
||||
PRInt32 aValue)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindInt32ByIndex(aParamIndex, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindInt64Parameter(PRUint32 aParamIndex,
|
||||
PRInt64 aValue)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindInt64ByIndex(aParamIndex, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindNullParameter(PRUint32 aParamIndex)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindNullByIndex(aParamIndex);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindBlobParameter(PRUint32 aParamIndex,
|
||||
const PRUint8 *aValue,
|
||||
PRUint32 aValueSize)
|
||||
{
|
||||
if (!mDBStatement)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
BindingParams *params = getParams();
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return params->BindBlobByIndex(aParamIndex, aValue, aValueSize);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
|
||||
{
|
||||
@ -680,17 +571,6 @@ Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::NewBindingParamsArray(mozIStorageBindingParamsArray **_array)
|
||||
{
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> array =
|
||||
new BindingParamsArray(this);
|
||||
NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
array.forget(_array);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::Execute()
|
||||
{
|
||||
@ -718,8 +598,9 @@ Statement::ExecuteStep(PRBool *_moreResults)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
BindingParamsArray::iterator row = mParamsArray->begin();
|
||||
nsCOMPtr<mozIStorageError> error;
|
||||
error = (*row)->bind(mDBStatement);
|
||||
nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
|
||||
do_QueryInterface(*row);
|
||||
nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
|
||||
if (error) {
|
||||
PRInt32 srv;
|
||||
(void)error->GetResult(&srv);
|
||||
@ -767,14 +648,6 @@ Statement::ExecuteStep(PRBool *_moreResults)
|
||||
return convertResultCode(srv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Statement::ExecuteAsync(mozIStorageStatementCallback *aCallback,
|
||||
mozIStoragePendingStatement **_stmt)
|
||||
{
|
||||
mozIStorageStatement *stmts[1] = {this};
|
||||
return mDBConnection->ExecuteAsync(stmts, 1, aCallback, _stmt);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::GetState(PRInt32 *_state)
|
||||
{
|
||||
@ -788,25 +661,6 @@ Statement::GetState(PRInt32 *_state)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::EscapeStringForLIKE(const nsAString &aValue,
|
||||
const PRUnichar aEscapeChar,
|
||||
nsAString &_escapedString)
|
||||
{
|
||||
const PRUnichar MATCH_ALL('%');
|
||||
const PRUnichar MATCH_ONE('_');
|
||||
|
||||
_escapedString.Truncate(0);
|
||||
|
||||
for (PRUint32 i = 0; i < aValue.Length(); i++) {
|
||||
if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
|
||||
aValue[i] == MATCH_ONE)
|
||||
_escapedString += aEscapeChar;
|
||||
_escapedString += aValue[i];
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::GetColumnDecltype(PRUint32 aParamIndex,
|
||||
nsACString &_declType)
|
||||
@ -821,7 +675,7 @@ Statement::GetColumnDecltype(PRUint32 aParamIndex,
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageValueArray
|
||||
//// mozIStorageValueArray (now part of mozIStorageStatement too)
|
||||
|
||||
NS_IMETHODIMP
|
||||
Statement::GetNumEntries(PRUint32 *_length)
|
||||
@ -845,19 +699,19 @@ Statement::GetTypeOfIndex(PRUint32 aIndex,
|
||||
int t = ::sqlite3_column_type(mDBStatement, aIndex);
|
||||
switch (t) {
|
||||
case SQLITE_INTEGER:
|
||||
*_type = VALUE_TYPE_INTEGER;
|
||||
*_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
*_type = VALUE_TYPE_FLOAT;
|
||||
*_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
*_type = VALUE_TYPE_TEXT;
|
||||
*_type = mozIStorageStatement::VALUE_TYPE_TEXT;
|
||||
break;
|
||||
case SQLITE_BLOB:
|
||||
*_type = VALUE_TYPE_BLOB;
|
||||
*_type = mozIStorageStatement::VALUE_TYPE_BLOB;
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
*_type = VALUE_TYPE_NULL;
|
||||
*_type = mozIStorageStatement::VALUE_TYPE_NULL;
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -924,7 +778,7 @@ Statement::GetUTF8String(PRUint32 aIndex,
|
||||
PRInt32 type;
|
||||
nsresult rv = GetTypeOfIndex(aIndex, &type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (type == VALUE_TYPE_NULL) {
|
||||
if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
|
||||
// NULL columns should have IsVod set to distinguis them from an empty
|
||||
// string.
|
||||
_value.Truncate(0);
|
||||
@ -947,7 +801,7 @@ Statement::GetString(PRUint32 aIndex,
|
||||
PRInt32 type;
|
||||
nsresult rv = GetTypeOfIndex(aIndex, &type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (type == VALUE_TYPE_NULL) {
|
||||
if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
|
||||
// NULL columns should have IsVod set to distinguis them from an empty
|
||||
// string.
|
||||
_value.Truncate(0);
|
||||
@ -1031,9 +885,17 @@ Statement::GetIsNull(PRUint32 aIndex,
|
||||
PRInt32 type;
|
||||
nsresult rv = GetTypeOfIndex(aIndex, &type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*_isNull = (type == VALUE_TYPE_NULL);
|
||||
*_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageBindingParams
|
||||
|
||||
BOILERPLATE_BIND_PROXIES(
|
||||
Statement,
|
||||
if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
|
||||
)
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
||||
|
@ -48,6 +48,8 @@
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozStorageStatementData.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStorageValueArray.h"
|
||||
#include "StorageBaseStatementInternal.h"
|
||||
|
||||
class nsIXPConnectJSObjectHolder;
|
||||
struct sqlite3_stmt;
|
||||
@ -56,14 +58,18 @@ namespace mozilla {
|
||||
namespace storage {
|
||||
class StatementJSHelper;
|
||||
class Connection;
|
||||
class BindingParams;
|
||||
|
||||
class Statement : public mozIStorageStatement
|
||||
, public mozIStorageValueArray
|
||||
, public StorageBaseStatementInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGESTATEMENT
|
||||
NS_DECL_MOZISTORAGEVALUEARRAY
|
||||
NS_DECL_MOZISTORAGEBASESTATEMENT
|
||||
NS_DECL_MOZISTORAGEBINDINGPARAMS
|
||||
// NS_DECL_MOZISTORAGEVALUEARRAY (methods in mozIStorageStatement)
|
||||
NS_DECL_STORAGEBASESTATEMENTINTERNAL
|
||||
|
||||
Statement();
|
||||
|
||||
@ -94,20 +100,9 @@ public:
|
||||
return mParamsArray.forget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the StatementData needed for asynchronous execution.
|
||||
*
|
||||
* @param _data
|
||||
* A reference to a StatementData object that will be populated upon
|
||||
* successful execution of this method.
|
||||
* @return an nsresult indicating success or failure.
|
||||
*/
|
||||
nsresult getAsynchronousStatementData(StatementData &_data);
|
||||
|
||||
private:
|
||||
~Statement();
|
||||
|
||||
nsRefPtr<Connection> mDBConnection;
|
||||
sqlite3_stmt *mDBStatement;
|
||||
PRUint32 mParamCount;
|
||||
PRUint32 mResultColumnCount;
|
||||
@ -118,7 +113,7 @@ private:
|
||||
* @return a pointer to the BindingParams object to use with our Bind*
|
||||
* method.
|
||||
*/
|
||||
BindingParams *getParams();
|
||||
mozIStorageBindingParams *getParams();
|
||||
|
||||
/**
|
||||
* Holds the array of parameters to bind to this statement when we execute
|
||||
@ -126,22 +121,6 @@ private:
|
||||
*/
|
||||
nsRefPtr<BindingParamsArray> mParamsArray;
|
||||
|
||||
/**
|
||||
* Holds a copy of mDBStatement that we can use asynchronously. Access to
|
||||
* this is serialized on the asynchronous thread, so it does not need to be
|
||||
* protected. We will finalize this statement in our destructor.
|
||||
*/
|
||||
sqlite3_stmt *mCachedAsyncStatement;
|
||||
|
||||
/**
|
||||
* Obtains the statement to use on the background thread.
|
||||
*
|
||||
* @param _stmt
|
||||
* An outparm where the new statement should be placed.
|
||||
* @return a SQLite result code indicating success or failure.
|
||||
*/
|
||||
int getAsyncStatement(sqlite3_stmt **_stmt);
|
||||
|
||||
/**
|
||||
* The following two members are only used with the JS helper. They cache
|
||||
* the row and params objects.
|
||||
@ -149,6 +128,16 @@ private:
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mStatementParamsHolder;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mStatementRowHolder;
|
||||
|
||||
/**
|
||||
* Internal version of finalize that allows us to tell it if it is being
|
||||
* called from the destructor so it can know not to dispatch events that
|
||||
* require a reference to us.
|
||||
*
|
||||
* @param aDestructing
|
||||
* Is the destructor calling?
|
||||
*/
|
||||
nsresult internalFinalize(bool aDestructing);
|
||||
|
||||
friend class StatementJSHelper;
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,9 @@
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "mozStorageBindingParamsArray.h"
|
||||
#include "mozIStorageBaseStatement.h"
|
||||
#include "mozStorageConnection.h"
|
||||
#include "StorageBaseStatementInternal.h"
|
||||
|
||||
struct sqlite3_stmt;
|
||||
|
||||
@ -57,7 +60,7 @@ class StatementData
|
||||
public:
|
||||
StatementData(sqlite3_stmt *aStatement,
|
||||
already_AddRefed<BindingParamsArray> aParamsArray,
|
||||
nsISupports *aStatementOwner)
|
||||
StorageBaseStatementInternal *aStatementOwner)
|
||||
: mStatement(aStatement)
|
||||
, mParamsArray(aParamsArray)
|
||||
, mStatementOwner(aStatementOwner)
|
||||
@ -73,24 +76,50 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
operator sqlite3_stmt *() const
|
||||
/**
|
||||
* Return the sqlite statement, fetching it from the storage statement. In
|
||||
* the case of AsyncStatements this may actually create the statement
|
||||
*/
|
||||
inline int getSqliteStatement(sqlite3_stmt **_stmt)
|
||||
{
|
||||
NS_ASSERTION(mStatement, "NULL sqlite3_stmt being handed off!");
|
||||
return mStatement;
|
||||
if (!mStatement) {
|
||||
int rc = mStatementOwner->getAsyncStatement(&mStatement);
|
||||
NS_ENSURE_TRUE(rc == SQLITE_OK, rc);
|
||||
}
|
||||
*_stmt = mStatement;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
operator BindingParamsArray *() const { return mParamsArray; }
|
||||
|
||||
/**
|
||||
* Provide the ability to coerce back to a sqlite3 * connection for purposes
|
||||
* of getting an error message out of it.
|
||||
*/
|
||||
operator sqlite3 *() const
|
||||
{
|
||||
return mStatementOwner->getOwner()->GetNativeConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and
|
||||
* clear all bindings to it. Then, NULL out the rest of our data.
|
||||
* clear all bindings to it. This is expected to occur on the async thread.
|
||||
*
|
||||
* We do not clear mParamsArray out because we only want to release
|
||||
* mParamsArray on the calling thread because of XPCVariant addref/release
|
||||
* thread-safety issues. The same holds for mStatementOwner which can be
|
||||
* holding such a reference chain as well.
|
||||
*/
|
||||
inline void finalize()
|
||||
{
|
||||
(void)::sqlite3_reset(mStatement);
|
||||
(void)::sqlite3_clear_bindings(mStatement);
|
||||
mStatement = NULL;
|
||||
mParamsArray = nsnull;
|
||||
mStatementOwner = nsnull;
|
||||
// In the AsyncStatement case we may never have populated mStatement if the
|
||||
// AsyncExecuteStatements got canceled or a failure occurred in constructing
|
||||
// the statement.
|
||||
if (mStatement) {
|
||||
(void)::sqlite3_reset(mStatement);
|
||||
(void)::sqlite3_clear_bindings(mStatement);
|
||||
mStatement = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +148,7 @@ private:
|
||||
* We hold onto a reference of the statement's owner so it doesn't get
|
||||
* destroyed out from under us.
|
||||
*/
|
||||
nsCOMPtr<nsISupports> mStatementOwner;
|
||||
nsCOMPtr<StorageBaseStatementInternal> mStatementOwner;
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
|
@ -74,15 +74,19 @@ stepFunc(JSContext *aCtx,
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
Statement *stmt = static_cast<Statement *>(wrapper->Native());
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> isStatement(do_QueryInterface(stmt));
|
||||
nsCOMPtr<mozIStorageStatement> isStatement(
|
||||
do_QueryInterface(wrapper->Native())
|
||||
);
|
||||
NS_ASSERTION(isStatement, "How is this not a statement?!");
|
||||
}
|
||||
#endif
|
||||
|
||||
Statement *stmt = static_cast<Statement *>(
|
||||
static_cast<mozIStorageStatement *>(wrapper->Native())
|
||||
);
|
||||
|
||||
PRBool hasMore = PR_FALSE;
|
||||
rv = stmt->ExecuteStep(&hasMore);
|
||||
if (NS_SUCCEEDED(rv) && !hasMore) {
|
||||
@ -208,15 +212,18 @@ StatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
if (!JSVAL_IS_STRING(aId))
|
||||
return NS_OK;
|
||||
|
||||
Statement *stmt = static_cast<Statement *>(aWrapper->Native());
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> isStatement(do_QueryInterface(stmt));
|
||||
nsCOMPtr<mozIStorageStatement> isStatement(
|
||||
do_QueryInterface(aWrapper->Native()));
|
||||
NS_ASSERTION(isStatement, "How is this not a statement?!");
|
||||
}
|
||||
#endif
|
||||
|
||||
Statement *stmt = static_cast<Statement *>(
|
||||
static_cast<mozIStorageStatement *>(aWrapper->Native())
|
||||
);
|
||||
|
||||
const char *propName = ::JS_GetStringBytes(JSVAL_TO_STRING(aId));
|
||||
if (::strcmp(propName, "row") == 0)
|
||||
return getRow(stmt, aCtx, aScopeObj, _result);
|
||||
|
@ -88,8 +88,10 @@ StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
if (JSVAL_IS_INT(aId)) {
|
||||
int idx = JSVAL_TO_INT(aId);
|
||||
|
||||
PRBool res = bindJSValue(aCtx, mStatement, idx, *_vp);
|
||||
NS_ENSURE_TRUE(res, NS_ERROR_UNEXPECTED);
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByIndex(idx, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else if (JSVAL_IS_STRING(aId)) {
|
||||
JSString *str = JSVAL_TO_STRING(aId);
|
||||
@ -98,12 +100,10 @@ StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
::JS_GetStringLength(str));
|
||||
|
||||
// check to see if there's a parameter with this name
|
||||
PRUint32 index;
|
||||
nsresult rv = mStatement->GetParameterIndex(name, &index);
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByName(name, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool res = bindJSValue(aCtx, mStatement, index, *_vp);
|
||||
NS_ENSURE_TRUE(res, NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
@ -107,7 +107,8 @@ StatementRow::GetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
else if (type == mozIStorageValueArray::VALUE_TYPE_TEXT) {
|
||||
PRUint32 bytes;
|
||||
const jschar *sval = reinterpret_cast<const jschar *>(
|
||||
mStatement->AsSharedWString(idx, &bytes)
|
||||
static_cast<mozIStorageStatement *>(mStatement)->
|
||||
AsSharedWString(idx, &bytes)
|
||||
);
|
||||
JSString *str = ::JS_NewUCStringCopyN(aCtx, sval, bytes / 2);
|
||||
if (!str) {
|
||||
@ -118,7 +119,8 @@ StatementRow::GetProperty(nsIXPConnectWrappedNative *aWrapper,
|
||||
}
|
||||
else if (type == mozIStorageValueArray::VALUE_TYPE_BLOB) {
|
||||
PRUint32 length;
|
||||
const PRUint8 *blob = mStatement->AsSharedBlob(idx, &length);
|
||||
const PRUint8 *blob = static_cast<mozIStorageStatement *>(mStatement)->
|
||||
AsSharedBlob(idx, &length);
|
||||
JSObject *obj = ::JS_NewArrayObject(aCtx, length, nsnull);
|
||||
if (!obj) {
|
||||
*_retval = PR_FALSE;
|
||||
|
@ -203,7 +203,9 @@ StatementWrapper::Call(nsIXPConnectWrappedNative *aWrapper,
|
||||
|
||||
// bind parameters
|
||||
for (int i = 0; i < (int)aArgc; i++) {
|
||||
if (!bindJSValue(aCtx, mStatement, i, aArgv[i])) {
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, aArgv[i]));
|
||||
if (!variant ||
|
||||
NS_FAILED(mStatement->BindByIndex(i, variant))) {
|
||||
*_retval = PR_FALSE;
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -25,6 +25,16 @@ will be enforcing them, so please obey them!
|
||||
|
||||
* Function declarations should include javadoc style comments.
|
||||
|
||||
* Javadoc @param tags should have the parameter description start on a new line
|
||||
aligned with the variable name. See the example below.
|
||||
|
||||
* Javadoc @return (note: non-plural) continuation lines should be lined up with
|
||||
the initial comment. See the example below.
|
||||
|
||||
* Javadoc @throws, like @param, should have the exception type on the same line
|
||||
as the @throws and the description on a new line indented to line up with
|
||||
the type of the exception.
|
||||
|
||||
* For function implementations, each argument should be on its own line.
|
||||
|
||||
* All variables should use camelCase.
|
||||
@ -39,7 +49,93 @@ will be enforcing them, so please obey them!
|
||||
|
||||
* Every else should be on a newline after a brace.
|
||||
|
||||
* Bracing should start on the line after a function and class definition.
|
||||
* Bracing should start on the line after a function and class definition. This
|
||||
goes for JavaScript code as well as C++ code.
|
||||
|
||||
* If a return value is not going to be checked, the return value should be
|
||||
explicitly casted to void (C style cast).
|
||||
|
||||
|
||||
BIG EXAMPLE:
|
||||
|
||||
*** Header ***
|
||||
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
...
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_storage_FILENAME_h_
|
||||
#define mozilla_storage_FILENAME_h_
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class Foo : public Bar
|
||||
, public Baz
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Brief function summary.
|
||||
*
|
||||
* @param aArg1
|
||||
* Description description description description description etc etc
|
||||
* next line of description.
|
||||
* @param aArg2
|
||||
* Description description description.
|
||||
* @return Description description description description description etc etc
|
||||
* next line of description.
|
||||
*
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* Okay, so this is for JavaScript code, but you probably get the
|
||||
* idea.
|
||||
*/
|
||||
int chew(int aArg1, int aArg2);
|
||||
};
|
||||
|
||||
} // storage
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_storage_FILENAME_h_
|
||||
|
||||
|
||||
*** Implementation ***
|
||||
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
...
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(
|
||||
Foo
|
||||
, IBar
|
||||
, IBaz
|
||||
)
|
||||
|
||||
Foo::Foo(
|
||||
LongArgumentLineThatWouldOtherwiseOverflow *aArgument1
|
||||
)
|
||||
: mField1(0)
|
||||
, mField2(0)
|
||||
{
|
||||
someMethodWithLotsOfParamsOrJustLongParameters(
|
||||
mLongFieldNameThatIsJustified,
|
||||
mMaybeThisOneIsLessJustifiedButBoyIsItLong,
|
||||
15
|
||||
);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Separate sections of the file like this
|
||||
|
||||
int
|
||||
Foo::chew(int aArg1, int aArg2)
|
||||
{
|
||||
(void)functionReturningAnIgnoredValue();
|
||||
|
||||
::functionFromGlobalNamespaceWithVoidReturnValue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ CPP_UNIT_TESTS = \
|
||||
test_statement_scoper.cpp \
|
||||
test_mutex.cpp \
|
||||
test_binding_params.cpp \
|
||||
test_true_async.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_DEBUG
|
||||
|
447
storage/test/test_true_async.cpp
Normal file
447
storage/test/test_true_async.cpp
Normal file
@ -0,0 +1,447 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** 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 the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andrew Sutherland <asutherland@asutherland.org> (Original Author)
|
||||
*
|
||||
* 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 "storage_test_harness.h"
|
||||
#include "prthread.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include "mozIStorageStatementCallback.h"
|
||||
#include "mozIStorageCompletionCallback.h"
|
||||
#include "mozIStorageBindingParamsArray.h"
|
||||
#include "mozIStorageBindingParams.h"
|
||||
#include "mozIStorageAsyncStatement.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStoragePendingStatement.h"
|
||||
|
||||
using mozilla::Monitor;
|
||||
using mozilla::MonitorAutoEnter;
|
||||
|
||||
/**
|
||||
* Verify that mozIStorageAsyncStatement's life-cycle never triggers a mutex on
|
||||
* the caller (generally main) thread. We do this by decorating the sqlite
|
||||
* mutex logic with our own code that checks what thread it is being invoked on
|
||||
* and sets a flag if it is invoked on the main thread. We are able to easily
|
||||
* decorate the SQLite mutex logic because SQLite allows us to retrieve the
|
||||
* current function pointers being used and then provide a new set.
|
||||
*/
|
||||
|
||||
/* ===== Mutex Watching ===== */
|
||||
|
||||
sqlite3_mutex_methods orig_mutex_methods;
|
||||
sqlite3_mutex_methods wrapped_mutex_methods;
|
||||
|
||||
bool mutex_used_on_watched_thread = false;
|
||||
PRThread *watched_thread = NULL;
|
||||
/**
|
||||
* Ugly hack to let us figure out what a connection's async thread is. If we
|
||||
* were MOZILLA_INTERNAL_API and linked as such we could just include
|
||||
* mozStorageConnection.h and just ask Connection directly. But that turns out
|
||||
* poorly.
|
||||
*
|
||||
* When the thread a mutex is invoked on isn't watched_thread we save it to this
|
||||
* variable.
|
||||
*/
|
||||
PRThread *last_non_watched_thread = NULL;
|
||||
|
||||
/**
|
||||
* Set a flag if the mutex is used on the thread we are watching, but always
|
||||
* call the real mutex function.
|
||||
*/
|
||||
extern "C" void wrapped_MutexEnter(sqlite3_mutex *mutex)
|
||||
{
|
||||
PRThread *curThread = ::PR_GetCurrentThread();
|
||||
if (curThread == watched_thread)
|
||||
mutex_used_on_watched_thread = true;
|
||||
else
|
||||
last_non_watched_thread = curThread;
|
||||
orig_mutex_methods.xMutexEnter(mutex);
|
||||
}
|
||||
|
||||
extern "C" int wrapped_MutexTry(sqlite3_mutex *mutex)
|
||||
{
|
||||
if (::PR_GetCurrentThread() == watched_thread)
|
||||
mutex_used_on_watched_thread = true;
|
||||
return orig_mutex_methods.xMutexTry(mutex);
|
||||
}
|
||||
|
||||
|
||||
#define do_check_ok(aInvoc) do_check_true((aInvoc) == SQLITE_OK)
|
||||
|
||||
void hook_sqlite_mutex()
|
||||
{
|
||||
// We need to initialize and teardown SQLite to get it to set up the
|
||||
// default mutex handlers for us so we can steal them and wrap them.
|
||||
sqlite3_initialize();
|
||||
sqlite3_shutdown();
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &orig_mutex_methods));
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped_mutex_methods));
|
||||
wrapped_mutex_methods.xMutexEnter = wrapped_MutexEnter;
|
||||
wrapped_mutex_methods.xMutexTry = wrapped_MutexTry;
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped_mutex_methods));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to clear the watch state and to set the watching against this thread.
|
||||
*
|
||||
* Check |mutex_used_on_watched_thread| to see if the mutex has fired since
|
||||
* this method was last called. Since we're talking about the current thread,
|
||||
* there are no race issues to be concerned about
|
||||
*/
|
||||
void watch_for_mutex_use_on_this_thread()
|
||||
{
|
||||
watched_thread = ::PR_GetCurrentThread();
|
||||
mutex_used_on_watched_thread = false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Event Loop Spinning
|
||||
|
||||
class AsyncStatementSpinner : public mozIStorageStatementCallback,
|
||||
public mozIStorageCompletionCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGESTATEMENTCALLBACK
|
||||
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
|
||||
|
||||
AsyncStatementSpinner();
|
||||
|
||||
void SpinUntilCompleted();
|
||||
|
||||
PRUint16 completionReason;
|
||||
|
||||
private:
|
||||
~AsyncStatementSpinner() {}
|
||||
volatile bool mCompleted;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(AsyncStatementSpinner,
|
||||
mozIStorageStatementCallback,
|
||||
mozIStorageCompletionCallback)
|
||||
|
||||
AsyncStatementSpinner::AsyncStatementSpinner()
|
||||
: completionReason(0)
|
||||
, mCompleted(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleError(mozIStorageError *aError)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleCompletion(PRUint16 aReason)
|
||||
{
|
||||
completionReason = aReason;
|
||||
mCompleted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::Complete()
|
||||
{
|
||||
mCompleted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AsyncStatementSpinner::SpinUntilCompleted()
|
||||
{
|
||||
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
|
||||
nsresult rv = NS_OK;
|
||||
PRBool processed = PR_TRUE;
|
||||
while (!mCompleted && NS_SUCCEEDED(rv)) {
|
||||
rv = thread->ProcessNextEvent(true, &processed);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Thread Wedgers
|
||||
|
||||
/**
|
||||
* A runnable that blocks until code on another thread invokes its unwedge
|
||||
* method. By dispatching this to a thread you can ensure that no subsequent
|
||||
* runnables dispatched to the thread will execute until you invoke unwedge.
|
||||
*
|
||||
* The wedger is self-dispatching, just construct it with its target.
|
||||
*/
|
||||
class ThreadWedger : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ThreadWedger(nsIEventTarget *aTarget)
|
||||
: mMonitor("thread wedger")
|
||||
, unwedged(false)
|
||||
{
|
||||
aTarget->Dispatch(this, aTarget->NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MonitorAutoEnter automon(mMonitor);
|
||||
|
||||
if (!unwedged)
|
||||
automon.Wait();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void unwedge()
|
||||
{
|
||||
MonitorAutoEnter automon(mMonitor);
|
||||
unwedged = true;
|
||||
automon.Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor mMonitor;
|
||||
bool unwedged;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Async Helpers
|
||||
|
||||
/**
|
||||
* Execute an async statement, blocking the main thread until we get the
|
||||
* callback completion notification.
|
||||
*/
|
||||
void
|
||||
blocking_async_execute(mozIStorageBaseStatement *stmt)
|
||||
{
|
||||
nsRefPtr<AsyncStatementSpinner> spinner(new AsyncStatementSpinner());
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> pendy;
|
||||
(void)stmt->ExecuteAsync(spinner, getter_AddRefs(pendy));
|
||||
spinner->SpinUntilCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke AsyncClose on the given connection, blocking the main thread until we
|
||||
* get the completion notification.
|
||||
*/
|
||||
void
|
||||
blocking_async_close(mozIStorageConnection *db)
|
||||
{
|
||||
nsRefPtr<AsyncStatementSpinner> spinner(new AsyncStatementSpinner());
|
||||
|
||||
db->AsyncClose(spinner);
|
||||
spinner->SpinUntilCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* A horrible hack to figure out what the connection's async thread is. By
|
||||
* creating a statement and async dispatching we can tell from the mutex who
|
||||
* is the async thread, PRThread style. Then we map that to an nsIThread.
|
||||
*/
|
||||
already_AddRefed<nsIThread>
|
||||
get_conn_async_thread(mozIStorageConnection *db)
|
||||
{
|
||||
// Make sure we are tracking the current thread as the watched thread
|
||||
watch_for_mutex_use_on_this_thread();
|
||||
|
||||
// - statement with nothing to bind
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("SELECT 1"),
|
||||
getter_AddRefs(stmt));
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
|
||||
nsCOMPtr<nsIThreadManager> threadMan =
|
||||
do_GetService("@mozilla.org/thread-manager;1");
|
||||
nsCOMPtr<nsIThread> asyncThread;
|
||||
threadMan->GetThreadFromPRThread(last_non_watched_thread,
|
||||
getter_AddRefs(asyncThread));
|
||||
return asyncThread.forget();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
void
|
||||
test_TrueAsyncStatement()
|
||||
{
|
||||
hook_sqlite_mutex();
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Start watching for forbidden mutex usage.
|
||||
watch_for_mutex_use_on_this_thread();
|
||||
|
||||
// - statement with nothing to bind
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - statement with something to bind ordinally
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (?)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
stmt->BindInt32Parameter(0, 1);
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - statement with something to bind by name
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (:id)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("id"), 2);
|
||||
paramsArray->AddParams(params);
|
||||
params = nsnull;
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nsnull;
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - now, make sure creating a sync statement does trigger our guard.
|
||||
// (If this doesn't happen, our test is bunk and it's important to know that.)
|
||||
nsCOMPtr<mozIStorageStatement> syncStmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM test"),
|
||||
getter_AddRefs(syncStmt));
|
||||
syncStmt->Finalize();
|
||||
do_check_true(mutex_used_on_watched_thread);
|
||||
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cancellation before a statement is run successfully stops the
|
||||
* statement from executing.
|
||||
*/
|
||||
void
|
||||
test_AsyncCancellation()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- wedge the thread
|
||||
nsCOMPtr<nsIThread> target(get_conn_async_thread(db));
|
||||
do_check_true(target);
|
||||
nsRefPtr<ThreadWedger> wedger (new ThreadWedger(target));
|
||||
|
||||
// -- create statements and cancel them
|
||||
// - async
|
||||
nsCOMPtr<mozIStorageAsyncStatement> asyncStmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE asyncTable (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(asyncStmt)
|
||||
);
|
||||
|
||||
nsRefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
|
||||
nsCOMPtr<mozIStoragePendingStatement> asyncPend;
|
||||
(void)asyncStmt->ExecuteAsync(asyncSpin, getter_AddRefs(asyncPend));
|
||||
do_check_true(asyncPend);
|
||||
asyncPend->Cancel();
|
||||
|
||||
// - sync
|
||||
nsCOMPtr<mozIStorageStatement> syncStmt;
|
||||
db->CreateStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE syncTable (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(syncStmt)
|
||||
);
|
||||
|
||||
nsRefPtr<AsyncStatementSpinner> syncSpin(new AsyncStatementSpinner());
|
||||
nsCOMPtr<mozIStoragePendingStatement> syncPend;
|
||||
(void)syncStmt->ExecuteAsync(syncSpin, getter_AddRefs(syncPend));
|
||||
do_check_true(syncPend);
|
||||
syncPend->Cancel();
|
||||
|
||||
// -- unwedge the async thread
|
||||
wedger->unwedge();
|
||||
|
||||
// -- verify that both statements report they were canceled
|
||||
asyncSpin->SpinUntilCompleted();
|
||||
do_check_true(asyncSpin->completionReason ==
|
||||
mozIStorageStatementCallback::REASON_CANCELED);
|
||||
|
||||
syncSpin->SpinUntilCompleted();
|
||||
do_check_true(syncSpin->completionReason ==
|
||||
mozIStorageStatementCallback::REASON_CANCELED);
|
||||
|
||||
// -- verify that neither statement constructed their tables
|
||||
nsresult rv;
|
||||
PRBool exists;
|
||||
rv = db->TableExists(NS_LITERAL_CSTRING("asyncTable"), &exists);
|
||||
do_check_true(rv == NS_OK);
|
||||
do_check_false(exists);
|
||||
rv = db->TableExists(NS_LITERAL_CSTRING("syncTable"), &exists);
|
||||
do_check_true(rv == NS_OK);
|
||||
do_check_false(exists);
|
||||
|
||||
// -- cleanup
|
||||
asyncStmt->Finalize();
|
||||
syncStmt->Finalize();
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
// this test must be first because it hooks the mutex mechanics
|
||||
test_TrueAsyncStatement,
|
||||
test_AsyncCancellation,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "true async statement"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
@ -75,6 +75,34 @@ function cleanup()
|
||||
try { dbFile.remove(false); } catch(e) { /* stupid windows box */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Use asyncClose to cleanup a connection. Synchronous by means of internally
|
||||
* spinning an event loop.
|
||||
*/
|
||||
function asyncCleanup()
|
||||
{
|
||||
let closed = false;
|
||||
|
||||
// close the connection
|
||||
print("*** Storage Tests: Trying to asyncClose!");
|
||||
getOpenedDatabase().asyncClose(function() { closed = true; });
|
||||
|
||||
let curThread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!closed)
|
||||
curThread.processNextEvent(true);
|
||||
|
||||
// we need to null out the database variable to get a new connection the next
|
||||
// time getOpenedDatabase is called
|
||||
gDBConn = null;
|
||||
|
||||
// removing test db
|
||||
print("*** Storage Tests: Trying to remove file!");
|
||||
var dbFile = getTestDB();
|
||||
if (dbFile.exists())
|
||||
try { dbFile.remove(false); } catch(e) { /* stupid windows box */ }
|
||||
}
|
||||
|
||||
function getService()
|
||||
{
|
||||
return Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
|
||||
@ -119,5 +147,117 @@ function createStatement(aSQL)
|
||||
return getOpenedDatabase().createStatement(aSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given function and assert that it throws an exception expressing
|
||||
* the provided error code in its 'result' attribute. JS function expressions
|
||||
* can be used to do this concisely.
|
||||
*
|
||||
* Example:
|
||||
* expectError(Cr.NS_ERROR_INVALID_ARG, function() explodingFunction());
|
||||
*
|
||||
* @param aErrorCode
|
||||
* The error code to expect from invocation of aFunction.
|
||||
* @param aFunction
|
||||
* The function to invoke and expect an XPCOM-style error from.
|
||||
*/
|
||||
function expectError(aErrorCode, aFunction)
|
||||
{
|
||||
let exceptionCaught = false;
|
||||
try {
|
||||
aFunction();
|
||||
}
|
||||
catch(e) {
|
||||
if (e.result != aErrorCode) {
|
||||
do_throw("Got an exception, but the result code was not the expected " +
|
||||
"one. Expected " + aErrorCode + ", got " + e.result);
|
||||
}
|
||||
exceptionCaught = true;
|
||||
}
|
||||
if (!exceptionCaught)
|
||||
do_throw(aFunction + " should have thrown an exception but did not!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a query synchronously and verify that we get back the expected results.
|
||||
*
|
||||
* @param aSQLString
|
||||
* The SQL string for the query.
|
||||
* @param aBind
|
||||
* The value to bind at index 0.
|
||||
* @param aResults
|
||||
* A list of the expected values returned in the sole result row.
|
||||
* Express blobs as lists.
|
||||
*/
|
||||
function verifyQuery(aSQLString, aBind, aResults)
|
||||
{
|
||||
let stmt = getOpenedDatabase().createStatement(aSQLString);
|
||||
stmt.bindByIndex(0, aBind);
|
||||
try {
|
||||
do_check_true(stmt.executeStep());
|
||||
let nCols = stmt.numEntries;
|
||||
if (aResults.length != nCols)
|
||||
do_throw("Expected " + aResults.length + " columns in result but " +
|
||||
"there are only " + aResults.length + "!");
|
||||
for (let iCol = 0; iCol < nCols; iCol++) {
|
||||
let expectedVal = aResults[iCol];
|
||||
let valType = stmt.getTypeOfIndex(iCol);
|
||||
if (expectedVal === null) {
|
||||
do_check_eq(stmt.VALUE_TYPE_NULL, valType);
|
||||
do_check_true(stmt.getIsNull(iCol));
|
||||
}
|
||||
else if (typeof(expectedVal) == "number") {
|
||||
if (Math.floor(expectedVal) == expectedVal) {
|
||||
do_check_eq(stmt.VALUE_TYPE_INTEGER, valType);
|
||||
do_check_eq(expectedVal, stmt.getInt32(iCol));
|
||||
}
|
||||
else {
|
||||
do_check_eq(stmt.VALUE_TYPE_FLOAT, valType);
|
||||
do_check_eq(expectedVal, stmt.getDouble(iCol));
|
||||
}
|
||||
}
|
||||
else if (typeof(expectedVal) == "string") {
|
||||
do_check_eq(stmt.VALUE_TYPE_TEXT, valType);
|
||||
do_check_eq(expectedVal, stmt.getUTF8String(iCol));
|
||||
}
|
||||
else { // blob
|
||||
do_check_eq(stmt.VALUE_TYPE_BLOB, valType);
|
||||
let count = { value: 0 }, blob = { value: null };
|
||||
stmt.getBlob(iCol, count, blob);
|
||||
do_check_eq(count.value, expectedVal.length);
|
||||
for (let i = 0; i < count.value; i++) {
|
||||
do_check_eq(expectedVal[i], blob.value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of rows in the able with the given name using a synchronous
|
||||
* query.
|
||||
*
|
||||
* @param aTableName
|
||||
* The name of the table.
|
||||
* @return The number of rows.
|
||||
*/
|
||||
function getTableRowCount(aTableName)
|
||||
{
|
||||
var currentRows = 0;
|
||||
var countStmt = getOpenedDatabase().createStatement(
|
||||
"SELECT COUNT(1) AS count FROM " + aTableName
|
||||
);
|
||||
try {
|
||||
do_check_true(countStmt.executeStep());
|
||||
currentRows = countStmt.row.count;
|
||||
}
|
||||
finally {
|
||||
countStmt.finalize();
|
||||
}
|
||||
return currentRows;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
|
@ -35,7 +35,10 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// This file tests the functionality of mozIStorageConnection::executeAsync
|
||||
/*
|
||||
* This file tests the functionality of mozIStorageConnection::executeAsync for
|
||||
* both mozIStorageStatement and mozIStorageAsyncStatement.
|
||||
*/
|
||||
|
||||
const INTEGER = 1;
|
||||
const TEXT = "this is test text";
|
||||
@ -63,7 +66,7 @@ function test_create_and_add()
|
||||
stmts[0].bindDoubleParameter(2, REAL);
|
||||
stmts[0].bindNullParameter(3);
|
||||
stmts[0].bindBlobParameter(4, BLOB, BLOB.length);
|
||||
stmts[1] = getOpenedDatabase().createStatement(
|
||||
stmts[1] = getOpenedDatabase().createAsyncStatement(
|
||||
"INSERT INTO test (string, number, nuller, blober) VALUES (?, ?, ?, ?)"
|
||||
);
|
||||
stmts[1].bindStringParameter(0, TEXT);
|
||||
@ -131,7 +134,7 @@ function test_create_and_add()
|
||||
function test_transaction_created()
|
||||
{
|
||||
let stmts = [];
|
||||
stmts[0] = getOpenedDatabase().createStatement(
|
||||
stmts[0] = getOpenedDatabase().createAsyncStatement(
|
||||
"BEGIN"
|
||||
);
|
||||
stmts[1] = getOpenedDatabase().createStatement(
|
||||
@ -169,13 +172,18 @@ function test_multiple_bindings_on_statements()
|
||||
const ITERATIONS = 5;
|
||||
|
||||
let stmts = [];
|
||||
let db = getOpenedDatabase();
|
||||
let sqlString = "INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, :blob)";
|
||||
// We run the same statement twice, and should insert 2 * AMOUNT_TO_ADD.
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
stmts[i] = getOpenedDatabase().createStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, :blob)"
|
||||
);
|
||||
let params = stmts[i].newBindingParamsArray()
|
||||
// alternate the type of statement we create
|
||||
if (i % 2)
|
||||
stmts[i] = db.createStatement(sqlString);
|
||||
else
|
||||
stmts[i] = db.createAsyncStatement(sqlString);
|
||||
|
||||
let params = stmts[i].newBindingParamsArray();
|
||||
for (let j = 0; j < AMOUNT_TO_ADD; j++) {
|
||||
let bp = params.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -456,7 +456,7 @@ nsFaviconService::ExpireAllFavicons()
|
||||
// then we do the same in the temp table. This is because the view UPDATE
|
||||
// trigger does not allow setting a NULL value to prevent dataloss.
|
||||
|
||||
mozIStorageStatement *stmts[] = {
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
GetStatement(mDBRemoveOnDiskReferences),
|
||||
GetStatement(mDBRemoveTempReferences),
|
||||
GetStatement(mDBRemoveAllFavicons),
|
||||
|
@ -5853,7 +5853,7 @@ nsNavHistory::VacuumDatabase()
|
||||
getter_AddRefs(journalToDefault));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozIStorageStatement *stmts[] = {
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
journalToMemory,
|
||||
vacuum,
|
||||
journalToDefault
|
||||
@ -5907,7 +5907,7 @@ nsNavHistory::DecayFrecency()
|
||||
getter_AddRefs(deleteAdaptive));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozIStorageStatement *stmts[] = {
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
decayFrecency,
|
||||
decayAdaptive,
|
||||
deleteAdaptive
|
||||
@ -6385,13 +6385,11 @@ nsNavHistory::ResultsAsList(mozIStorageStatement* statement,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aResults)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(statement, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool hasMore = PR_FALSE;
|
||||
while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
|
||||
nsRefPtr<nsNavHistoryResultNode> result;
|
||||
rv = RowToResult(row, aOptions, getter_AddRefs(result));
|
||||
rv = RowToResult(statement, aOptions, getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aResults->AppendObject(result);
|
||||
}
|
||||
@ -6778,7 +6776,7 @@ nsNavHistory::GetRedirectFor(const nsACString& aDestination,
|
||||
// or full visit.
|
||||
|
||||
nsresult
|
||||
nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
|
||||
nsNavHistory::RowToResult(mozIStorageStatement* aRow,
|
||||
nsNavHistoryQueryOptions* aOptions,
|
||||
nsNavHistoryResultNode** aResult)
|
||||
{
|
||||
|
@ -255,7 +255,7 @@ public:
|
||||
|
||||
// Take a row of kGetInfoIndex_* columns and construct a ResultNode.
|
||||
// The row must contain the full set of columns.
|
||||
nsresult RowToResult(mozIStorageValueArray* aRow,
|
||||
nsresult RowToResult(mozIStorageStatement* aRow,
|
||||
nsNavHistoryQueryOptions* aOptions,
|
||||
nsNavHistoryResultNode** aResult);
|
||||
nsresult QueryRowToResult(PRInt64 aItemId, const nsACString& aURI,
|
||||
|
Loading…
Reference in New Issue
Block a user