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:
Andrew Sutherland 2010-03-24 00:32:40 -07:00
parent f6e896ba0c
commit 805fe0e096
44 changed files with 3986 additions and 1169 deletions

View File

@ -65,6 +65,8 @@ XPIDLSRCS = \
mozIStorageBindingParamsArray.idl \
mozIStorageBindingParams.idl \
mozIStorageCompletionCallback.idl \
mozIStorageBaseStatement.idl \
mozIStorageAsyncStatement.idl \
$(NULL)
EXPORTS_NAMESPACES = mozilla

View 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;
*/
};

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

View File

@ -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,

View File

@ -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.
*/

View File

@ -39,8 +39,9 @@
#include "nsISupports.idl"
#include "mozIStorageValueArray.idl"
interface mozIStorageConnection;
interface mozIStorageValueArray;
interface nsIArray;
interface nsIVariant;

View File

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

View File

@ -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 {

View 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_

View File

@ -75,6 +75,10 @@ CPPSRCS = \
mozStoragePrivateHelpers.cpp \
mozStorageBindingParamsArray.cpp \
mozStorageBindingParams.cpp \
mozStorageAsyncStatement.cpp \
mozStorageAsyncStatementJSHelper.cpp \
mozStorageAsyncStatementParams.cpp \
StorageBaseStatementInternal.cpp \
SQLCollations.cpp \
$(NULL)

View 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

View 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_

View 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

View 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_

View File

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

View File

@ -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

View 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

View 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_

View 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

View 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_

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View 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"

View File

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

View File

@ -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

View File

@ -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),

View File

@ -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)
{

View File

@ -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,