LCOV - code coverage report
Current view: top level - storage - mozStorageAsyncStatement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 99 136 72.8 %
Date: 2017-07-14 16:53:18 Functions: 23 60 38.3 %
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 et lcs=trail\:.,tab\:>~ :
       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 <limits.h>
       8             : #include <stdio.h>
       9             : 
      10             : #include "nsError.h"
      11             : #include "nsMemory.h"
      12             : #include "nsProxyRelease.h"
      13             : #include "nsThreadUtils.h"
      14             : #include "nsIClassInfoImpl.h"
      15             : #include "Variant.h"
      16             : 
      17             : #include "mozIStorageError.h"
      18             : 
      19             : #include "mozStorageBindingParams.h"
      20             : #include "mozStorageConnection.h"
      21             : #include "mozStorageAsyncStatementJSHelper.h"
      22             : #include "mozStorageAsyncStatementParams.h"
      23             : #include "mozStoragePrivateHelpers.h"
      24             : #include "mozStorageStatementRow.h"
      25             : #include "mozStorageStatement.h"
      26             : #include "nsDOMClassInfo.h"
      27             : 
      28             : #include "mozilla/Logging.h"
      29             : 
      30             : extern mozilla::LazyLogModule gStorageLog;
      31             : 
      32             : namespace mozilla {
      33             : namespace storage {
      34             : 
      35             : ////////////////////////////////////////////////////////////////////////////////
      36             : //// nsIClassInfo
      37             : 
      38           1 : NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement,
      39             :                             mozIStorageAsyncStatement,
      40             :                             mozIStorageBaseStatement,
      41             :                             mozIStorageBindingParams,
      42             :                             mozilla::storage::StorageBaseStatementInternal)
      43             : 
      44             : class AsyncStatementClassInfo : public nsIClassInfo
      45             : {
      46             : public:
      47             :   constexpr AsyncStatementClassInfo() {}
      48             : 
      49             :   NS_DECL_ISUPPORTS_INHERITED
      50             : 
      51             :   NS_IMETHOD
      52           1 :   GetInterfaces(uint32_t *_count, nsIID ***_array) override
      53             :   {
      54           1 :     return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
      55             :   }
      56             : 
      57             :   NS_IMETHOD
      58           7 :   GetScriptableHelper(nsIXPCScriptable **_helper) override
      59             :   {
      60           7 :     static AsyncStatementJSHelper sJSHelper;
      61           7 :     *_helper = &sJSHelper;
      62           7 :     return NS_OK;
      63             :   }
      64             : 
      65             :   NS_IMETHOD
      66           0 :   GetContractID(char **_contractID) override
      67             :   {
      68           0 :     *_contractID = nullptr;
      69           0 :     return NS_OK;
      70             :   }
      71             : 
      72             :   NS_IMETHOD
      73           0 :   GetClassDescription(char **_desc) override
      74             :   {
      75           0 :     *_desc = nullptr;
      76           0 :     return NS_OK;
      77             :   }
      78             : 
      79             :   NS_IMETHOD
      80           0 :   GetClassID(nsCID **_id) override
      81             :   {
      82           0 :     *_id = nullptr;
      83           0 :     return NS_OK;
      84             :   }
      85             : 
      86             :   NS_IMETHOD
      87           7 :   GetFlags(uint32_t *_flags) override
      88             :   {
      89           7 :     *_flags = 0;
      90           7 :     return NS_OK;
      91             :   }
      92             : 
      93             :   NS_IMETHOD
      94           0 :   GetClassIDNoAlloc(nsCID *_cid) override
      95             :   {
      96           0 :     return NS_ERROR_NOT_AVAILABLE;
      97             :   }
      98             : };
      99             : 
     100          13 : NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() { return 2; }
     101          10 : NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() { return 1; }
     102          10 : NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
     103             : 
     104             : static AsyncStatementClassInfo sAsyncStatementClassInfo;
     105             : 
     106             : ////////////////////////////////////////////////////////////////////////////////
     107             : //// AsyncStatement
     108             : 
     109          16 : AsyncStatement::AsyncStatement()
     110             : : StorageBaseStatementInternal()
     111          16 : , mFinalized(false)
     112             : {
     113          16 : }
     114             : 
     115             : nsresult
     116          16 : AsyncStatement::initialize(Connection *aDBConnection,
     117             :                            sqlite3 *aNativeConnection,
     118             :                            const nsACString &aSQLStatement)
     119             : {
     120          16 :   MOZ_ASSERT(aDBConnection, "No database connection given!");
     121          16 :   MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(), "Database connection should be valid");
     122          16 :   MOZ_ASSERT(aNativeConnection, "No native connection given!");
     123             : 
     124          16 :   mDBConnection = aDBConnection;
     125          16 :   mNativeConnection = aNativeConnection;
     126          16 :   mSQLString = aSQLStatement;
     127             : 
     128          16 :   MOZ_LOG(gStorageLog, LogLevel::Debug, ("Inited async statement '%s' (0x%p)",
     129             :                                       mSQLString.get(), this));
     130             : 
     131             : #ifdef DEBUG
     132             :   // We want to try and test for LIKE and that consumers are using
     133             :   // escapeStringForLIKE instead of just trusting user input.  The idea to
     134             :   // check to see if they are binding a parameter after like instead of just
     135             :   // using a string.  We only do this in debug builds because it's expensive!
     136          16 :   const nsCaseInsensitiveCStringComparator c;
     137          16 :   nsACString::const_iterator start, end, e;
     138          16 :   aSQLStatement.BeginReading(start);
     139          16 :   aSQLStatement.EndReading(end);
     140          16 :   e = end;
     141          18 :   while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
     142             :     // We have a LIKE in here, so we perform our tests
     143             :     // FindInReadable moves the iterator, so we have to get a new one for
     144             :     // each test we perform.
     145           1 :     nsACString::const_iterator s1, s2, s3;
     146           1 :     s1 = s2 = s3 = start;
     147             : 
     148           4 :     if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
     149           3 :           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
     150           1 :           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
     151             :       // At this point, we didn't find a LIKE statement followed by ?, :,
     152             :       // or @, all of which are valid characters for binding a parameter.
     153             :       // We will warn the consumer that they may not be safely using LIKE.
     154             :       NS_WARNING("Unsafe use of LIKE detected!  Please ensure that you "
     155             :                  "are using mozIStorageAsyncStatement::escapeStringForLIKE "
     156             :                  "and that you are binding that result to the statement "
     157           0 :                  "to prevent SQL injection attacks.");
     158             :     }
     159             : 
     160             :     // resetting start and e
     161           1 :     start = e;
     162           1 :     e = end;
     163             :   }
     164             : #endif
     165             : 
     166          16 :   return NS_OK;
     167             : }
     168             : 
     169             : mozIStorageBindingParams *
     170          10 : AsyncStatement::getParams()
     171             : {
     172             :   nsresult rv;
     173             : 
     174             :   // If we do not have an array object yet, make it.
     175          10 :   if (!mParamsArray) {
     176          12 :     nsCOMPtr<mozIStorageBindingParamsArray> array;
     177           6 :     rv = NewBindingParamsArray(getter_AddRefs(array));
     178           6 :     NS_ENSURE_SUCCESS(rv, nullptr);
     179             : 
     180           6 :     mParamsArray = static_cast<BindingParamsArray *>(array.get());
     181             :   }
     182             : 
     183             :   // If there isn't already any rows added, we'll have to add one to use.
     184          10 :   if (mParamsArray->length() == 0) {
     185          18 :     RefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
     186           6 :     NS_ENSURE_TRUE(params, nullptr);
     187             : 
     188           6 :     rv = mParamsArray->AddParams(params);
     189           6 :     NS_ENSURE_SUCCESS(rv, nullptr);
     190             : 
     191             :     // We have to unlock our params because AddParams locks them.  This is safe
     192             :     // because no reference to the params object was, or ever will be given out.
     193           6 :     params->unlock(nullptr);
     194             : 
     195             :     // We also want to lock our array at this point - we don't want anything to
     196             :     // be added to it.
     197           6 :     mParamsArray->lock();
     198             :   }
     199             : 
     200          10 :   return *mParamsArray->begin();
     201             : }
     202             : 
     203             : /**
     204             :  * If we are here then we know there are no pending async executions relying on
     205             :  * us (StatementData holds a reference to us; this also goes for our own
     206             :  * AsyncStatementFinalizer which proxies its release to the calling thread) and
     207             :  * so it is always safe to destroy our sqlite3_stmt if one exists.  We can be
     208             :  * destroyed on the caller thread by garbage-collection/reference counting or on
     209             :  * the async thread by the last execution of a statement that already lost its
     210             :  * main-thread refs.
     211             :  */
     212           4 : AsyncStatement::~AsyncStatement()
     213             : {
     214           2 :   destructorAsyncFinalize();
     215             : 
     216             :   // If we are getting destroyed on the wrong thread, proxy the connection
     217             :   // release to the right thread.  I'm not sure why we do this.
     218           2 :   bool onCallingThread = false;
     219           2 :   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
     220           2 :   if (!onCallingThread) {
     221             :     // NS_ProxyRelase only magic forgets for us if mDBConnection is an
     222             :     // nsCOMPtr.  Which it is not; it's an nsRefPtr.
     223           4 :     nsCOMPtr<nsIThread> targetThread(mDBConnection->threadOpenedOn);
     224             :     NS_ProxyRelease(
     225             :       "AsyncStatement::mDBConnection",
     226           2 :       targetThread, mDBConnection.forget());
     227             :   }
     228           2 : }
     229             : 
     230             : ////////////////////////////////////////////////////////////////////////////////
     231             : //// nsISupports
     232             : 
     233         122 : NS_IMPL_ADDREF(AsyncStatement)
     234         103 : NS_IMPL_RELEASE(AsyncStatement)
     235             : 
     236          90 : NS_INTERFACE_MAP_BEGIN(AsyncStatement)
     237          90 :   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
     238          65 :   NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
     239          62 :   NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
     240          62 :   NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
     241          42 :   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
     242           7 :     foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
     243             :   }
     244             :   else
     245          35 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
     246          28 : NS_INTERFACE_MAP_END
     247             : 
     248             : 
     249             : ////////////////////////////////////////////////////////////////////////////////
     250             : //// StorageBaseStatementInternal
     251             : 
     252             : Connection *
     253           0 : AsyncStatement::getOwner()
     254             : {
     255           0 :   return mDBConnection;
     256             : }
     257             : 
     258             : int
     259          11 : AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
     260             : {
     261             : #ifdef DEBUG
     262             :   // Make sure we are never called on the connection's owning thread.
     263          11 :   bool onOpenedThread = false;
     264          11 :   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
     265          11 :   NS_ASSERTION(!onOpenedThread,
     266             :                "We should only be called on the async thread!");
     267             : #endif
     268             : 
     269          11 :   if (!mAsyncStatement) {
     270          10 :     int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
     271          10 :                                              &mAsyncStatement);
     272          10 :     if (rc != SQLITE_OK) {
     273           0 :       MOZ_LOG(gStorageLog, LogLevel::Error,
     274             :              ("Sqlite statement prepare error: %d '%s'", rc,
     275             :               ::sqlite3_errmsg(mNativeConnection)));
     276           0 :       MOZ_LOG(gStorageLog, LogLevel::Error,
     277             :              ("Statement was: '%s'", mSQLString.get()));
     278           0 :       *_stmt = nullptr;
     279           0 :       return rc;
     280             :     }
     281          10 :     MOZ_LOG(gStorageLog, LogLevel::Debug, ("Initialized statement '%s' (0x%p)",
     282             :                                         mSQLString.get(),
     283             :                                         mAsyncStatement));
     284             :   }
     285             : 
     286          11 :   *_stmt = mAsyncStatement;
     287          11 :   return SQLITE_OK;
     288             : }
     289             : 
     290             : nsresult
     291          11 : AsyncStatement::getAsynchronousStatementData(StatementData &_data)
     292             : {
     293          11 :   if (mFinalized)
     294           0 :     return NS_ERROR_UNEXPECTED;
     295             : 
     296             :   // Pass null for the sqlite3_stmt; it will be requested on demand from the
     297             :   // async thread.
     298          11 :   _data = StatementData(nullptr, bindingParamsArray(), this);
     299             : 
     300          11 :   return NS_OK;
     301             : }
     302             : 
     303             : already_AddRefed<mozIStorageBindingParams>
     304           0 : AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
     305             : {
     306           0 :   if (mFinalized)
     307           0 :     return nullptr;
     308             : 
     309           0 :   nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
     310           0 :   return params.forget();
     311             : }
     312             : 
     313             : 
     314             : ////////////////////////////////////////////////////////////////////////////////
     315             : //// mozIStorageAsyncStatement
     316             : 
     317             : // (nothing is specific to mozIStorageAsyncStatement)
     318             : 
     319             : ////////////////////////////////////////////////////////////////////////////////
     320             : //// StorageBaseStatementInternal
     321             : 
     322             : // proxy to StorageBaseStatementInternal using its define helper.
     323          15 : MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
     324             :   AsyncStatement,
     325             :   if (mFinalized) return NS_ERROR_UNEXPECTED;)
     326             : 
     327             : NS_IMETHODIMP
     328           4 : AsyncStatement::Finalize()
     329             : {
     330           4 :   if (mFinalized)
     331           0 :     return NS_OK;
     332             : 
     333           4 :   mFinalized = true;
     334             : 
     335           4 :   MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s'",
     336             :                                       mSQLString.get()));
     337             : 
     338           4 :   asyncFinalize();
     339             : 
     340             :   // Release the params holder, so it can release the reference to us.
     341           4 :   mStatementParamsHolder = nullptr;
     342             : 
     343           4 :   return NS_OK;
     344             : }
     345             : 
     346             : NS_IMETHODIMP
     347           0 : AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
     348             : {
     349           0 :   if (mFinalized)
     350           0 :     return NS_ERROR_UNEXPECTED;
     351             : 
     352           0 :   BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
     353           0 :   if (array->getOwner() != this)
     354           0 :     return NS_ERROR_UNEXPECTED;
     355             : 
     356           0 :   if (array->length() == 0)
     357           0 :     return NS_ERROR_UNEXPECTED;
     358             : 
     359           0 :   mParamsArray = array;
     360           0 :   mParamsArray->lock();
     361             : 
     362           0 :   return NS_OK;
     363             : }
     364             : 
     365             : NS_IMETHODIMP
     366          10 : AsyncStatement::GetState(int32_t *_state)
     367             : {
     368          10 :   if (mFinalized)
     369           0 :     *_state = MOZ_STORAGE_STATEMENT_INVALID;
     370             :   else
     371          10 :     *_state = MOZ_STORAGE_STATEMENT_READY;
     372             : 
     373          10 :   return NS_OK;
     374             : }
     375             : 
     376             : ////////////////////////////////////////////////////////////////////////////////
     377             : //// mozIStorageBindingParams
     378             : 
     379          10 : BOILERPLATE_BIND_PROXIES(
     380             :   AsyncStatement,
     381             :   if (mFinalized) return NS_ERROR_UNEXPECTED;
     382             : )
     383             : 
     384             : } // namespace storage
     385             : } // namespace mozilla

Generated by: LCOV version 1.13