storage/src/StorageBaseStatementInternal.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 sts=2 expandtab
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "StorageBaseStatementInternal.h"
     9 #include "nsProxyRelease.h"
    11 #include "mozStorageBindingParamsArray.h"
    12 #include "mozStorageStatementData.h"
    13 #include "mozStorageAsyncStatementExecution.h"
    15 namespace mozilla {
    16 namespace storage {
    18 ////////////////////////////////////////////////////////////////////////////////
    19 //// Local Classes
    21 /**
    22  * Used to finalize an asynchronous statement on the background thread.
    23  */
    24 class AsyncStatementFinalizer : public nsRunnable
    25 {
    26 public:
    27   /**
    28    * Constructor for the event.
    29    *
    30    * @param aStatement
    31    *        We need the AsyncStatement to be able to get at the sqlite3_stmt;
    32    *        we only access/create it on the async thread.
    33    * @param aConnection
    34    *        We need the connection to know what thread to release the statement
    35    *        on.  We release the statement on that thread since releasing the
    36    *        statement might end up releasing the connection too.
    37    */
    38   AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement,
    39                           Connection *aConnection)
    40   : mStatement(aStatement)
    41   , mConnection(aConnection)
    42   {
    43   }
    45   NS_IMETHOD Run()
    46   {
    47     if (mStatement->mAsyncStatement) {
    48       (void)::sqlite3_finalize(mStatement->mAsyncStatement);
    49       mStatement->mAsyncStatement = nullptr;
    50     }
    51     (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
    52     return NS_OK;
    53   }
    54 private:
    55   nsRefPtr<StorageBaseStatementInternal> mStatement;
    56   nsRefPtr<Connection> mConnection;
    57 };
    59 /**
    60  * Finalize a sqlite3_stmt on the background thread for a statement whose
    61  * destructor was invoked and the statement was non-null.
    62  */
    63 class LastDitchSqliteStatementFinalizer : public nsRunnable
    64 {
    65 public:
    66   /**
    67    * Event constructor.
    68    *
    69    * @param aConnection
    70    *        Used to keep the connection alive.  If we failed to do this, it
    71    *        is possible that the statement going out of scope invoking us
    72    *        might have the last reference to the connection and so trigger
    73    *        an attempt to close the connection which is doomed to fail
    74    *        (because the asynchronous execution thread must exist which will
    75    *        trigger the failure case).
    76    * @param aStatement
    77    *        The sqlite3_stmt to finalize.  This object takes ownership /
    78    *        responsibility for the instance and all other references to it
    79    *        should be forgotten.
    80    */
    81   LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection,
    82                                     sqlite3_stmt *aStatement)
    83   : mConnection(aConnection)
    84   , mAsyncStatement(aStatement)
    85   {
    86     NS_PRECONDITION(aConnection, "You must provide a Connection");
    87   }
    89   NS_IMETHOD Run()
    90   {
    91     (void)::sqlite3_finalize(mAsyncStatement);
    92     mAsyncStatement = nullptr;
    94     // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease
    95     // template helpers.
    96     Connection *rawConnection = nullptr;
    97     mConnection.swap(rawConnection);
    98     (void)::NS_ProxyRelease(
    99       rawConnection->threadOpenedOn,
   100       NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection));
   101     return NS_OK;
   102   }
   103 private:
   104   nsRefPtr<Connection> mConnection;
   105   sqlite3_stmt *mAsyncStatement;
   106 };
   108 ////////////////////////////////////////////////////////////////////////////////
   109 //// StorageBaseStatementInternal
   111 StorageBaseStatementInternal::StorageBaseStatementInternal()
   112 : mAsyncStatement(nullptr)
   113 {
   114 }
   116 void
   117 StorageBaseStatementInternal::asyncFinalize()
   118 {
   119   nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
   120   if (target) {
   121     // Attempt to finalize asynchronously
   122     nsCOMPtr<nsIRunnable> event =
   123       new AsyncStatementFinalizer(this, mDBConnection);
   125     // Dispatch. Note that dispatching can fail, typically if
   126     // we have a race condition with asyncClose(). It's ok,
   127     // let asyncClose() win.
   128     (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
   129   }
   130   // If we cannot get the background thread,
   131   // mozStorageConnection::AsyncClose() has already been called and
   132   // the statement either has been or will be cleaned up by
   133   // internalClose().
   134 }
   136 void
   137 StorageBaseStatementInternal::destructorAsyncFinalize()
   138 {
   139   if (!mAsyncStatement)
   140     return;
   142   // If we reach this point, our owner has not finalized this
   143   // statement, yet we are being destructed. If possible, we want to
   144   // auto-finalize it early, to release the resources early.
   145   nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
   146   if (target) {
   147     // If we can get the async execution target, we can indeed finalize
   148     // the statement, as the connection is still open.
   149     bool isAsyncThread = false;
   150     (void)target->IsOnCurrentThread(&isAsyncThread);
   152     nsCOMPtr<nsIRunnable> event =
   153       new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
   154     if (isAsyncThread) {
   155       (void)event->Run();
   156     } else {
   157       (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
   158     }
   159   }
   161   // We might not be able to dispatch to the background thread,
   162   // presumably because it is being shutdown. Since said shutdown will
   163   // finalize the statement, we just need to clean-up around here.
   164   mAsyncStatement = nullptr;
   165 }
   167 NS_IMETHODIMP
   168 StorageBaseStatementInternal::NewBindingParamsArray(
   169   mozIStorageBindingParamsArray **_array
   170 )
   171 {
   172   nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
   173   NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
   175   array.forget(_array);
   176   return NS_OK;
   177 }
   179 NS_IMETHODIMP
   180 StorageBaseStatementInternal::ExecuteAsync(
   181   mozIStorageStatementCallback *aCallback,
   182   mozIStoragePendingStatement **_stmt
   183 )
   184 {
   185   // We used to call Connection::ExecuteAsync but it takes a
   186   // mozIStorageBaseStatement signature because it is also a public API.  Since
   187   // our 'this' has no static concept of mozIStorageBaseStatement and Connection
   188   // would just QI it back across to a StorageBaseStatementInternal and the
   189   // actual logic is very simple, we now roll our own.
   190   nsTArray<StatementData> stmts(1);
   191   StatementData data;
   192   nsresult rv = getAsynchronousStatementData(data);
   193   NS_ENSURE_SUCCESS(rv, rv);
   194   NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
   196   // Dispatch to the background
   197   return AsyncExecuteStatements::execute(stmts, mDBConnection,
   198                                          mNativeConnection, aCallback, _stmt);
   199 }
   201 NS_IMETHODIMP
   202 StorageBaseStatementInternal::EscapeStringForLIKE(
   203   const nsAString &aValue,
   204   const char16_t aEscapeChar,
   205   nsAString &_escapedString
   206 )
   207 {
   208   const char16_t MATCH_ALL('%');
   209   const char16_t MATCH_ONE('_');
   211   _escapedString.Truncate(0);
   213   for (uint32_t i = 0; i < aValue.Length(); i++) {
   214     if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
   215         aValue[i] == MATCH_ONE) {
   216       _escapedString += aEscapeChar;
   217     }
   218     _escapedString += aValue[i];
   219   }
   220   return NS_OK;
   221 }
   223 } // namespace storage
   224 } // namespace mozilla

mercurial