LCOV - code coverage report
Current view: top level - storage - StorageBaseStatementInternal.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 63 69 91.3 %
Date: 2017-07-14 16:53:18 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "StorageBaseStatementInternal.h"
       8             : 
       9             : #include "nsProxyRelease.h"
      10             : 
      11             : #include "mozStorageBindingParamsArray.h"
      12             : #include "mozStorageStatementData.h"
      13             : #include "mozStorageAsyncStatementExecution.h"
      14             : 
      15             : namespace mozilla {
      16             : namespace storage {
      17             : 
      18             : ////////////////////////////////////////////////////////////////////////////////
      19             : //// Local Classes
      20             : 
      21             : /**
      22             :  * Used to finalize an asynchronous statement on the background thread.
      23             :  */
      24          12 : class AsyncStatementFinalizer : public Runnable
      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           4 :   AsyncStatementFinalizer(StorageBaseStatementInternal* aStatement,
      39             :                           Connection* aConnection)
      40           4 :     : Runnable("storage::AsyncStatementFinalizer")
      41             :     , mStatement(aStatement)
      42           4 :     , mConnection(aConnection)
      43             :   {
      44           4 :   }
      45             : 
      46           4 :   NS_IMETHOD Run() override
      47             :   {
      48           4 :     if (mStatement->mAsyncStatement) {
      49           4 :       sqlite3_finalize(mStatement->mAsyncStatement);
      50           4 :       mStatement->mAsyncStatement = nullptr;
      51             :     }
      52             : 
      53           8 :     nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn);
      54             :     NS_ProxyRelease(
      55           4 :       "AsyncStatementFinalizer::mStatement", targetThread, mStatement.forget());
      56           8 :     return NS_OK;
      57             :   }
      58             : private:
      59             :   RefPtr<StorageBaseStatementInternal> mStatement;
      60             :   RefPtr<Connection> mConnection;
      61             : };
      62             : 
      63             : /**
      64             :  * Finalize a sqlite3_stmt on the background thread for a statement whose
      65             :  * destructor was invoked and the statement was non-null.
      66             :  */
      67           6 : class LastDitchSqliteStatementFinalizer : public Runnable
      68             : {
      69             : public:
      70             :   /**
      71             :    * Event constructor.
      72             :    *
      73             :    * @param aConnection
      74             :    *        Used to keep the connection alive.  If we failed to do this, it
      75             :    *        is possible that the statement going out of scope invoking us
      76             :    *        might have the last reference to the connection and so trigger
      77             :    *        an attempt to close the connection which is doomed to fail
      78             :    *        (because the asynchronous execution thread must exist which will
      79             :    *        trigger the failure case).
      80             :    * @param aStatement
      81             :    *        The sqlite3_stmt to finalize.  This object takes ownership /
      82             :    *        responsibility for the instance and all other references to it
      83             :    *        should be forgotten.
      84             :    */
      85           2 :   LastDitchSqliteStatementFinalizer(RefPtr<Connection>& aConnection,
      86             :                                     sqlite3_stmt* aStatement)
      87           2 :     : Runnable("storage::LastDitchSqliteStatementFinalizer")
      88             :     , mConnection(aConnection)
      89           2 :     , mAsyncStatement(aStatement)
      90             :   {
      91           2 :     NS_PRECONDITION(aConnection, "You must provide a Connection");
      92           2 :   }
      93             : 
      94           2 :   NS_IMETHOD Run() override
      95             :   {
      96           2 :     (void)::sqlite3_finalize(mAsyncStatement);
      97           2 :     mAsyncStatement = nullptr;
      98             : 
      99           4 :     nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn);
     100             :     (void)::NS_ProxyRelease(
     101             :       "LastDitchSqliteStatementFinalizer::mConnection",
     102           2 :       target, mConnection.forget());
     103           4 :     return NS_OK;
     104             :   }
     105             : private:
     106             :   RefPtr<Connection> mConnection;
     107             :   sqlite3_stmt *mAsyncStatement;
     108             : };
     109             : 
     110             : ////////////////////////////////////////////////////////////////////////////////
     111             : //// StorageBaseStatementInternal
     112             : 
     113          45 : StorageBaseStatementInternal::StorageBaseStatementInternal()
     114          45 : : mAsyncStatement(nullptr)
     115             : {
     116          45 : }
     117             : 
     118             : void
     119           4 : StorageBaseStatementInternal::asyncFinalize()
     120             : {
     121           4 :   nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
     122           4 :   if (target) {
     123             :     // Attempt to finalize asynchronously
     124             :     nsCOMPtr<nsIRunnable> event =
     125          12 :       new AsyncStatementFinalizer(this, mDBConnection);
     126             : 
     127             :     // Dispatch. Note that dispatching can fail, typically if
     128             :     // we have a race condition with asyncClose(). It's ok,
     129             :     // let asyncClose() win.
     130           4 :     (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
     131             :   }
     132             :   // If we cannot get the background thread,
     133             :   // mozStorageConnection::AsyncClose() has already been called and
     134             :   // the statement either has been or will be cleaned up by
     135             :   // internalClose().
     136           4 : }
     137             : 
     138             : void
     139           2 : StorageBaseStatementInternal::destructorAsyncFinalize()
     140             : {
     141           2 :   if (!mAsyncStatement)
     142           0 :     return;
     143             : 
     144           2 :   bool isOwningThread = false;
     145           2 :   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&isOwningThread);
     146           2 :   if (isOwningThread) {
     147             :     // If we are the owning thread (currently that means we're also the
     148             :     // main thread), then we can get the async target and just dispatch
     149             :     // to it.
     150           0 :     nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
     151           0 :     if (target) {
     152             :       nsCOMPtr<nsIRunnable> event =
     153           0 :         new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
     154           0 :       (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
     155             :     }
     156             :   } else {
     157             :     // If we're not the owning thread, assume we're the async thread, and
     158             :     // just run the statement.
     159             :     nsCOMPtr<nsIRunnable> event =
     160           4 :       new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
     161           2 :     (void)event->Run();
     162             :   }
     163             : 
     164             : 
     165             :   // We might not be able to dispatch to the background thread,
     166             :   // presumably because it is being shutdown. Since said shutdown will
     167             :   // finalize the statement, we just need to clean-up around here.
     168           2 :   mAsyncStatement = nullptr;
     169             : }
     170             : 
     171             : NS_IMETHODIMP
     172          19 : StorageBaseStatementInternal::NewBindingParamsArray(
     173             :   mozIStorageBindingParamsArray **_array
     174             : )
     175             : {
     176          38 :   nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
     177          19 :   NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
     178             : 
     179          19 :   array.forget(_array);
     180          19 :   return NS_OK;
     181             : }
     182             : 
     183             : NS_IMETHODIMP
     184           8 : StorageBaseStatementInternal::ExecuteAsync(
     185             :   mozIStorageStatementCallback *aCallback,
     186             :   mozIStoragePendingStatement **_stmt
     187             : )
     188             : {
     189             :   // We used to call Connection::ExecuteAsync but it takes a
     190             :   // mozIStorageBaseStatement signature because it is also a public API.  Since
     191             :   // our 'this' has no static concept of mozIStorageBaseStatement and Connection
     192             :   // would just QI it back across to a StorageBaseStatementInternal and the
     193             :   // actual logic is very simple, we now roll our own.
     194          16 :   nsTArray<StatementData> stmts(1);
     195          16 :   StatementData data;
     196           8 :   nsresult rv = getAsynchronousStatementData(data);
     197           8 :   NS_ENSURE_SUCCESS(rv, rv);
     198           8 :   NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
     199             : 
     200             :   // Dispatch to the background
     201           8 :   return AsyncExecuteStatements::execute(stmts, mDBConnection,
     202           8 :                                          mNativeConnection, aCallback, _stmt);
     203             : }
     204             : 
     205             : NS_IMETHODIMP
     206           1 : StorageBaseStatementInternal::EscapeStringForLIKE(
     207             :   const nsAString &aValue,
     208             :   const char16_t aEscapeChar,
     209             :   nsAString &_escapedString
     210             : )
     211             : {
     212           1 :   const char16_t MATCH_ALL('%');
     213           1 :   const char16_t MATCH_ONE('_');
     214             : 
     215           1 :   _escapedString.Truncate(0);
     216             : 
     217          10 :   for (uint32_t i = 0; i < aValue.Length(); i++) {
     218          18 :     if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
     219           9 :         aValue[i] == MATCH_ONE) {
     220           0 :       _escapedString += aEscapeChar;
     221             :     }
     222           9 :     _escapedString += aValue[i];
     223             :   }
     224           1 :   return NS_OK;
     225             : }
     226             : 
     227             : } // namespace storage
     228             : } // namespace mozilla

Generated by: LCOV version 1.13