Bug 545625 - back-fork ifdef'd mozStorage of bug 507414 into comm-central or mozilla-1.9.2 for TB 3.1 release. Now that the patch for bug 507414 has landed on trunk, update our copy to be identical to that one (plus our patches to make it build for mail/). rs=Standard8 from previous authorization for this patch.
authorAndrew Sutherland <asutherland@asutherland.org>
Wed, 24 Mar 2010 05:56:26 -0700
changeset 5249 d4ef96917d25868732847a35f941f973f7001c10
parent 5248 3147aad42dfe780debee6da7fdb2f83dffd55f95
child 5250 5528d77d0a2156aa89827f4c1ba403601330c501
push idunknown
push userunknown
push dateunknown
reviewersStandard8
bugs545625, 507414
Bug 545625 - back-fork ifdef'd mozStorage of bug 507414 into comm-central or mozilla-1.9.2 for TB 3.1 release. Now that the patch for bug 507414 has landed on trunk, update our copy to be identical to that one (plus our patches to make it build for mail/). rs=Standard8 from previous authorization for this patch.
mail/build.mk
mail/makefiles.sh
storage-backport/public/mozIStorageBaseStatement.idl
storage-backport/public/mozIStorageBindingParamsArray.idl
storage-backport/public/mozIStorageConnection.idl
storage-backport/public/storage.h
storage-backport/src/StorageBaseStatementInternal.h
storage-backport/src/mozStorageAsyncStatement.cpp
storage-backport/src/mozStorageAsyncStatementExecution.cpp
storage-backport/src/mozStorageAsyncStatementExecution.h
storage-backport/src/mozStorageAsyncStatementJSHelper.cpp
storage-backport/src/mozStorageBindingParams.cpp
storage-backport/src/mozStorageBindingParams.h
storage-backport/src/mozStorageBindingParamsArray.cpp
storage-backport/src/mozStorageConnection.cpp
storage-backport/src/mozStorageStatement.cpp
storage-backport/src/mozStorageStatement.h
storage-backport/src/mozStorageStatementData.h
storage-backport/src/mozStorageStatementJSHelper.cpp
storage-backport/style.txt
storage-backport/test/test_true_async.cpp
storage-backport/test/unit/test_statement_executeAsync.js
--- a/mail/build.mk
+++ b/mail/build.mk
@@ -37,16 +37,17 @@
 
 ifndef COMM_BUILD # Mozilla Makefile
 
 ifndef LIBXUL_SDK
 include $(topsrcdir)/toolkit/toolkit-tiers.mk
 endif
 
 ## storage backfork.
+# this is temporary until we branch for MOZILLA_1_9_2_BRANCH
 # replace toolkit's storage with our own
 tier_gecko_dirs := $(patsubst storage,../storage-backport,$(tier_gecko_dirs))
 # necko also has a dependency...
 tier_necko_dirs := $(patsubst storage/public,../storage-backport/public,$(tier_necko_dirs))
 
 
 TIERS += app
 
--- a/mail/makefiles.sh
+++ b/mail/makefiles.sh
@@ -59,17 +59,18 @@ mail/test/mozmill/Makefile
 mail/themes/Makefile
 mail/themes/gnomestripe/Makefile
 mail/themes/pinstripe/Makefile
 mail/themes/qute/Makefile
 $MOZ_BRANDING_DIRECTORY/Makefile
 $MOZ_BRANDING_DIRECTORY/locales/Makefile
 "
 
-# storage-backport stuff
+# storage-backport stuff.
+# this is temporary until we branch for MOZILLA_1_9_2_BRANCH
 add_makefiles "
 storage-backport/Makefile
 storage-backport/public/Makefile
 storage-backport/src/Makefile
 storage-backport/build/Makefile
 storage-backport/test/Makefile
 "
 fi
--- a/storage-backport/public/mozIStorageBaseStatement.idl
+++ b/storage-backport/public/mozIStorageBaseStatement.idl
@@ -75,17 +75,20 @@ interface mozIStorageBaseStatement : moz
   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 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);
--- a/storage-backport/public/mozIStorageBindingParamsArray.idl
+++ b/storage-backport/public/mozIStorageBindingParamsArray.idl
@@ -36,27 +36,32 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface mozIStorageBindingParams;
 
-[scriptable, uuid(e676e1a3-1dc6-4802-ac03-291fa9de7f93)]
+[scriptable, uuid(67eea5c3-4881-41ff-b0fe-09f2356aeadb)]
 interface mozIStorageBindingParamsArray : nsISupports {
   /**
    * Creates a new mozIStorageBindingParams object that can be added to this
    * array.
    *
-   * @returns a mozIStorageBindingParams object that can be used to specify
-   *          parameters that need to be bound.
+   * @return a mozIStorageBindingParams object that can be used to specify
+   *         parameters that need to be bound.
    */
   mozIStorageBindingParams newBindingParams();
 
   /**
    * Adds the parameters to the end of this array.
    *
    * @param aParameters
    *        The parameters to add to this array.
    */
   void addParams(in mozIStorageBindingParams aParameters);
+
+  /**
+   * The number of mozIStorageBindingParams this object contains.
+   */
+  readonly attribute unsigned long length;
 };
--- a/storage-backport/public/mozIStorageConnection.idl
+++ b/storage-backport/public/mozIStorageConnection.idl
@@ -252,18 +252,19 @@ interface mozIStorageConnection : nsISup
    *
    * @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.
+   * @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
 
   /**
--- a/storage-backport/public/storage.h
+++ b/storage-backport/public/storage.h
@@ -38,29 +38,31 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_storage_h_
 #define mozilla_storage_h_
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Public Interfaces
 
+#include "mozStorageCID.h"
 #include "mozIStorageAggregateFunction.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageError.h"
 #include "mozIStorageFunction.h"
 #include "mozIStoragePendingStatement.h"
 #include "mozIStorageProgressHandler.h"
 #include "mozIStorageResultSet.h"
 #include "mozIStorageRow.h"
 #include "mozIStorageService.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageStatementCallback.h"
+#include "mozIStorageBindingParamsArray.h"
+#include "mozIStorageBindingParams.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Native Language Helpers
 
 #include "mozStorageHelper.h"
-#include "mozStorageCID.h"
 
 #include "mozilla/storage/Variant.h"
 
 #endif // mozilla_storage_h_
--- a/storage-backport/src/StorageBaseStatementInternal.h
+++ b/storage-backport/src/StorageBaseStatementInternal.h
@@ -232,21 +232,26 @@ NS_DEFINE_STATIC_IID_ACCESSOR(StorageBas
  * 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.
+ * @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;                  \
--- a/storage-backport/src/mozStorageAsyncStatement.cpp
+++ b/storage-backport/src/mozStorageAsyncStatement.cpp
@@ -419,16 +419,19 @@ AsyncStatement::BindParameters(mozIStora
 {
   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)
@@ -441,12 +444,13 @@ AsyncStatement::GetState(PRInt32 *_state
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// mozIStorageBindingParams
 
 BOILERPLATE_BIND_PROXIES(
   AsyncStatement, 
-  if (mFinalized) return NS_ERROR_UNEXPECTED;)
+  if (mFinalized) return NS_ERROR_UNEXPECTED;
+)
 
 } // namespace storage
 } // namespace mozilla
--- a/storage-backport/src/mozStorageAsyncStatementExecution.cpp
+++ b/storage-backport/src/mozStorageAsyncStatementExecution.cpp
@@ -239,19 +239,20 @@ AsyncExecuteStatements::shouldNotify()
 }
 
 bool
 AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
                                                        bool aLastStatement)
 {
   mMutex.AssertNotCurrentThreadOwns();
 
-  sqlite3_stmt *aStatement;
+  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.
   bool continueProcessing = true;
   BindingParamsArray::iterator itr = paramsArray->begin();
   BindingParamsArray::iterator end = paramsArray->end();
   while (itr != end && continueProcessing) {
     // Bind the data to our statement.
--- a/storage-backport/src/mozStorageAsyncStatementExecution.h
+++ b/storage-backport/src/mozStorageAsyncStatementExecution.h
@@ -234,21 +234,20 @@ private:
    * This includes the following variables:
    *   - mCancelRequested is only set on the calling thread while the lock is
    *     held.  It is always read from within the lock on the background thread,
    *     but not on the calling thread (see shouldNotify for why).
    */
   Mutex &mMutex;
 
   /**
-   * The wrapped SQLite recursive connection mutex used by sqlite3_step.  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.
+   * 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
 } // namespace mozilla
 
 #endif // _mozStorageAsyncStatementExecution_h_
--- a/storage-backport/src/mozStorageAsyncStatementJSHelper.cpp
+++ b/storage-backport/src/mozStorageAsyncStatementJSHelper.cpp
@@ -10,18 +10,17 @@
  *
  * 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
- * Mozilla Corporation.
+ * 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
--- a/storage-backport/src/mozStorageBindingParams.cpp
+++ b/storage-backport/src/mozStorageBindingParams.cpp
@@ -198,23 +198,22 @@ AsyncBindingParams::iterateOverNamedPara
   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,
-                                              PromiseFlatCString(name).get());
+                                              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,
-                                  PromiseFlatCString(errMsg).get());
+    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,
--- a/storage-backport/src/mozStorageBindingParams.h
+++ b/storage-backport/src/mozStorageBindingParams.h
@@ -67,19 +67,20 @@ public:
    * Locks the parameters and prevents further modification to it (such as
    * binding more elements to it).
    */
   void lock();
 
   /**
    * Unlocks the parameters and allows modification to it again.
    *
-   * @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.
+   * @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.
    */
   void unlock(Statement *aOwningStatement);
 
   /**
    * @returns the pointer to the owning BindingParamsArray.  Used by a
    *          BindingParamsArray to verify that we belong to it when added.
    */
   const mozIStorageBindingParamsArray *getOwner() const;
--- a/storage-backport/src/mozStorageBindingParamsArray.cpp
+++ b/storage-backport/src/mozStorageBindingParamsArray.cpp
@@ -107,10 +107,17 @@ BindingParamsArray::AddParams(mozIStorag
   NS_ENSURE_TRUE(mArray.AppendElement(params), NS_ERROR_OUT_OF_MEMORY);
 
   // Lock the parameters only after we've successfully added them.
   params->lock();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+BindingParamsArray::GetLength(PRUint32 *_length)
+{
+  *_length = length();
+  return NS_OK;
+}
+
 } // namespace storage
 } // namespace mozilla
--- a/storage-backport/src/mozStorageConnection.cpp
+++ b/storage-backport/src/mozStorageConnection.cpp
@@ -255,50 +255,45 @@ aggregateFunctionFinalHelper(sqlite3_con
 
 namespace {
 
 class AsyncCloseConnection : public nsRunnable
 {
 public:
   AsyncCloseConnection(Connection *aConnection,
                        nsIEventTarget *aCallingThread,
-                       nsIRunnable *aCallbackEvent,
-                       nsIThread *aAsyncThread)
+                       nsIRunnable *aCallbackEvent)
   : mConnection(aConnection)
   , mCallingThread(aCallingThread)
   , mCallbackEvent(aCallbackEvent)
-  , mAsyncThread(aAsyncThread)
   {
   }
 
   NS_METHOD Run()
   {
     // This event is first dispatched to the background thread to ensure that
     // all pending asynchronous events are completed, and then back to the
     // calling thread to actually close and notify.
     PRBool onCallingThread = PR_FALSE;
     (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     if (!onCallingThread) {
       (void)mCallingThread->Dispatch(this, NS_DISPATCH_NORMAL);
       return NS_OK;
     }
 
-    if (mConnection)
-      (void)mConnection->internalClose();
+    (void)mConnection->internalClose();
     if (mCallbackEvent)
       (void)mCallingThread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
-    mAsyncThread->Shutdown();
 
     return NS_OK;
   }
 private:
   nsCOMPtr<Connection> mConnection;
   nsCOMPtr<nsIEventTarget> mCallingThread;
   nsCOMPtr<nsIRunnable> mCallbackEvent;
-  nsCOMPtr<nsIThread> mAsyncThread;
 };
 
 } // anonymous namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Connection
 
 Connection::Connection(Service *aService)
@@ -312,32 +307,16 @@ Connection::Connection(Service *aService
 , mStorageService(aService)
 {
   mFunctions.Init();
 }
 
 Connection::~Connection()
 {
   (void)Close();
-
-  // If we know about an async execution thread then we need to take steps to
-  // trigger its shutdown.  (AsyncClose is the only other code to trigger
-  // something like this and it nulls out our reference so there is no risk of
-  // double triggering.)
-  if (mAsyncExecutionThread) {
-    // Note: Obviously, we can't tell it about us since we are dying.  We also
-    // skimp on using a mutex since there's no point.
-    nsCOMPtr<nsIRunnable> closeEvent =
-      new AsyncCloseConnection(nsnull, NS_GetCurrentThread(), nsnull,
-                               mAsyncExecutionThread);
-    if (!closeEvent)
-      return;
-
-    (void)mAsyncExecutionThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
-  }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(
   Connection,
   mozIStorageConnection
 )
 
 nsIEventTarget *
@@ -640,25 +619,18 @@ Connection::AsyncClose(mozIStorageComple
   // Create our callback event if we were given a callback.
   nsCOMPtr<nsIRunnable> completeEvent;
   if (aCallback) {
     completeEvent = newCompletionEvent(aCallback);
     NS_ENSURE_TRUE(completeEvent, NS_ERROR_OUT_OF_MEMORY);
   }
 
   // Create and dispatch our close event to the background thread.
-  nsCOMPtr<nsIRunnable> closeEvent;
-  {
-    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
-    closeEvent = new AsyncCloseConnection(this, NS_GetCurrentThread(),
-                                          completeEvent,
-                                          mAsyncExecutionThread);
-    // forget about the async thread (this is why we're holding the mutex)
-    mAsyncExecutionThread = nsnull;
-  }
+  nsCOMPtr<nsIRunnable> closeEvent =
+    new AsyncCloseConnection(this, NS_GetCurrentThread(), completeEvent);
   NS_ENSURE_TRUE(closeEvent, NS_ERROR_OUT_OF_MEMORY);
 
   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
--- a/storage-backport/src/mozStorageStatement.cpp
+++ b/storage-backport/src/mozStorageStatement.cpp
@@ -381,33 +381,33 @@ Statement::Clone(mozIStorageStatement **
 
 NS_IMETHODIMP
 Statement::Finalize()
 {
   return internalFinalize(false);
 }
 
 nsresult
-Statement::internalFinalize(bool destructing)
+Statement::internalFinalize(bool aDestructing)
 {
   if (!mDBStatement)
     return NS_OK;
 
 #ifdef PR_LOGGING
   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
                                       ::sqlite3_sql(mDBStatement)));
 #endif
 
   int srv = ::sqlite3_finalize(mDBStatement);
   mDBStatement = 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 (destructing)
+    if (aDestructing)
       internalAsyncFinalize();
     else
       asyncFinalize();
   }
 
   // We are considered dead at this point, so any wrappers for row or params
   // need to lose their reference to us.
   if (mStatementParamsHolder) {
@@ -557,16 +557,19 @@ Statement::BindParameters(mozIStorageBin
 {
   if (!mDBStatement)
     return NS_ERROR_NOT_INITIALIZED;
 
   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
 Statement::Execute()
@@ -886,12 +889,13 @@ Statement::GetIsNull(PRUint32 aIndex,
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// mozIStorageBindingParams
 
 BOILERPLATE_BIND_PROXIES(
   Statement, 
-  if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;)
+  if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
+)
 
 } // namespace storage
 } // namespace mozilla
--- a/storage-backport/src/mozStorageStatement.h
+++ b/storage-backport/src/mozStorageStatement.h
@@ -128,19 +128,20 @@ 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 destructing Is the destructor calling?
+   * @param aDestructing
+   *        Is the destructor calling?
    */
-  nsresult internalFinalize(bool destructing);
+  nsresult internalFinalize(bool aDestructing);
 
     friend class StatementJSHelper;
 };
 
 } // storage
 } // mozilla
 
 #endif // _mozStorageStatement_h_
--- a/storage-backport/src/mozStorageStatementData.h
+++ b/storage-backport/src/mozStorageStatementData.h
@@ -79,18 +79,17 @@ public:
   /**
    * 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)
   {
     if (!mStatement) {
       int rc = mStatementOwner->getAsyncStatement(&mStatement);
-      if (rc != SQLITE_OK)
-        return rc;
+      NS_ENSURE_TRUE(rc == SQLITE_OK, rc);
     }
     *_stmt = mStatement;
     return SQLITE_OK;
   }
 
   operator BindingParamsArray *() const { return mParamsArray; }
 
   /**
--- a/storage-backport/src/mozStorageStatementJSHelper.cpp
+++ b/storage-backport/src/mozStorageStatementJSHelper.cpp
@@ -69,29 +69,29 @@ stepFunc(JSContext *aCtx,
   nsresult rv = xpc->GetWrappedNativeOfJSObject(
     aCtx, JS_THIS_OBJECT(aCtx, _vp), getter_AddRefs(wrapper)
   );
   if (NS_FAILED(rv)) {
     ::JS_ReportError(aCtx, "mozIStorageStatement::step() could not obtain native statement");
     return JS_FALSE;
   }
 
-  Statement *stmt = static_cast<Statement *>(
-    static_cast<mozIStorageStatement *>(wrapper->Native())
-  );
-
 #ifdef DEBUG
   {
     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) {
     *_vp = JSVAL_FALSE;
     (void)stmt->Reset();
     return JS_TRUE;
   }
 
@@ -207,28 +207,28 @@ StatementJSHelper::GetProperty(nsIXPConn
                                JSObject *aScopeObj,
                                jsval aId,
                                jsval *_result,
                                PRBool *_retval)
 {
   if (!JSVAL_IS_STRING(aId))
     return NS_OK;
 
-  Statement *stmt = static_cast<Statement *>(
-    static_cast<mozIStorageStatement *>(aWrapper->Native())
-  );
-
 #ifdef DEBUG
   {
     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);
 
   if (::strcmp(propName, "params") == 0)
     return getParams(stmt, aCtx, aScopeObj, _result);
 
   return NS_OK;
--- a/storage-backport/style.txt
+++ b/storage-backport/style.txt
@@ -26,16 +26,20 @@ will be enforcing them, so please obey t
 * 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.
 
 * The use of bool is encouraged whenever the variable does not have the
   potential to go through xpconnect.
 
 * For pointer variable types, include a space after the type before the asterisk
@@ -63,30 +67,34 @@ BIG EXAMPLE:
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_storage_FILENAME_h_
 #define mozilla_storage_FILENAME_h_
 
 namespace mozilla {
 namespace storage {
 
-class Foo : Bar
-          , Baz
+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_
@@ -107,16 +115,21 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(
 )
 
 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)
 {
--- a/storage-backport/test/test_true_async.cpp
+++ b/storage-backport/test/test_true_async.cpp
@@ -248,43 +248,46 @@ private:
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Async Helpers
 
 /**
  * Execute an async statement, blocking the main thread until we get the
  * callback completion notification.
  */
-void blocking_async_execute(mozIStorageBaseStatement *stmt)
+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)
+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)
+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"),
@@ -313,34 +316,37 @@ test_TrueAsyncStatement()
 
   // 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));
+    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));
+    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));
+    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);
@@ -374,29 +380,31 @@ test_AsyncCancellation()
   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));
+    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));
+    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
--- a/storage-backport/test/unit/test_statement_executeAsync.js
+++ b/storage-backport/test/unit/test_statement_executeAsync.js
@@ -603,16 +603,17 @@ function test_bind_multiple_rows_by_inde
   for (let i = 0; i < AMOUNT_TO_ADD; i++) {
     let bp = array.newBindingParams();
     bp.bindByIndex(0, INTEGER);
     bp.bindByIndex(1, TEXT);
     bp.bindByIndex(2, REAL);
     bp.bindByIndex(3, null);
     bp.bindBlobByIndex(4, BLOB, BLOB.length);
     array.addParams(bp);
+    do_check_eq(array.length, i + 1);
   }
   stmt.bindParameters(array);
 
   let rowCount = getTableRowCount("test");
   execAsync(stmt);
   do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
   stmt.finalize();
   run_next_test();
@@ -629,16 +630,17 @@ function test_bind_multiple_rows_by_name
   for (let i = 0; i < AMOUNT_TO_ADD; i++) {
     let bp = array.newBindingParams();
     bp.bindByName("int", INTEGER);
     bp.bindByName("text", TEXT);
     bp.bindByName("real", REAL);
     bp.bindByName("null", null);
     bp.bindBlobByName("blob", BLOB, BLOB.length);
     array.addParams(bp);
+    do_check_eq(array.length, i + 1);
   }
   stmt.bindParameters(array);
 
   let rowCount = getTableRowCount("test");
   execAsync(stmt);
   do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
   stmt.finalize();
   run_next_test();
@@ -885,16 +887,34 @@ function test_not_right_owning_statement
   expectError(Cr.NS_ERROR_UNEXPECTED,
               function() stmt2.bindParameters(array1));
 
   stmt1.finalize();
   stmt2.finalize();
   run_next_test();
 }
 
+function test_bind_empty_array()
+{
+  let stmt = makeTestStatement(
+    "INSERT INTO test (id) " +
+    "VALUES (:int)"
+  );
+
+  let paramsArray = stmt.newBindingParamsArray();
+
+  // We should not be able to bind this array to the statement because it is
+  // empty.
+  expectError(Cr.NS_ERROR_UNEXPECTED,
+              function() stmt.bindParameters(paramsArray));
+
+  stmt.finalize();
+  run_next_test();
+}
+
 function test_multiple_results()
 {
   let expectedResults = getTableRowCount("test");
   // Sanity check - we should have more than one result, but let's be sure.
   do_check_true(expectedResults > 1);
 
   // Now check that we get back two rows of data from our async query.
   let stmt = makeTestStatement("SELECT * FROM test");
@@ -960,16 +980,17 @@ var tests =
   test_bind_out_of_bounds_sync_immediate,
   test_bind_out_of_bounds_async_deferred,
   test_bind_no_such_name_sync_immediate,
   test_bind_no_such_name_async_deferred,
   test_bind_bogus_type_by_index,
   test_bind_bogus_type_by_name,
   test_bind_params_already_locked,
   test_bind_params_array_already_locked,
+  test_bind_empty_array,
   test_no_binding_params_from_locked_array,
   test_not_right_owning_array,
   test_not_right_owning_statement,
   test_multiple_results,
 ];
 let index = 0;
 
 const STARTING_UNIQUE_ID = 2;