storage/src/mozStorageStatement.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 #include <limits.h>
     8 #include <stdio.h>
    10 #include "nsError.h"
    11 #include "nsMemory.h"
    12 #include "nsThreadUtils.h"
    13 #include "nsIClassInfoImpl.h"
    14 #include "nsIProgrammingLanguage.h"
    15 #include "Variant.h"
    17 #include "mozIStorageError.h"
    19 #include "mozStorageBindingParams.h"
    20 #include "mozStorageConnection.h"
    21 #include "mozStorageStatementJSHelper.h"
    22 #include "mozStoragePrivateHelpers.h"
    23 #include "mozStorageStatementParams.h"
    24 #include "mozStorageStatementRow.h"
    25 #include "mozStorageStatement.h"
    26 #include "GeckoProfiler.h"
    27 #include "nsDOMClassInfo.h"
    29 #include "prlog.h"
    32 #ifdef PR_LOGGING
    33 extern PRLogModuleInfo* gStorageLog;
    34 #endif
    36 namespace mozilla {
    37 namespace storage {
    39 ////////////////////////////////////////////////////////////////////////////////
    40 //// nsIClassInfo
    42 NS_IMPL_CI_INTERFACE_GETTER(Statement,
    43                             mozIStorageStatement,
    44                             mozIStorageBaseStatement,
    45                             mozIStorageBindingParams,
    46                             mozIStorageValueArray,
    47                             mozilla::storage::StorageBaseStatementInternal)
    49 class StatementClassInfo : public nsIClassInfo
    50 {
    51 public:
    52   MOZ_CONSTEXPR StatementClassInfo() {}
    54   NS_DECL_ISUPPORTS_INHERITED
    56   NS_IMETHODIMP
    57   GetInterfaces(uint32_t *_count, nsIID ***_array)
    58   {
    59     return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array);
    60   }
    62   NS_IMETHODIMP
    63   GetHelperForLanguage(uint32_t aLanguage, nsISupports **_helper)
    64   {
    65     if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
    66       static StatementJSHelper sJSHelper;
    67       *_helper = &sJSHelper;
    68       return NS_OK;
    69     }
    71     *_helper = nullptr;
    72     return NS_OK;
    73   }
    75   NS_IMETHODIMP
    76   GetContractID(char **_contractID)
    77   {
    78     *_contractID = nullptr;
    79     return NS_OK;
    80   }
    82   NS_IMETHODIMP
    83   GetClassDescription(char **_desc)
    84   {
    85     *_desc = nullptr;
    86     return NS_OK;
    87   }
    89   NS_IMETHODIMP
    90   GetClassID(nsCID **_id)
    91   {
    92     *_id = nullptr;
    93     return NS_OK;
    94   }
    96   NS_IMETHODIMP
    97   GetImplementationLanguage(uint32_t *_language)
    98   {
    99     *_language = nsIProgrammingLanguage::CPLUSPLUS;
   100     return NS_OK;
   101   }
   103   NS_IMETHODIMP
   104   GetFlags(uint32_t *_flags)
   105   {
   106     *_flags = 0;
   107     return NS_OK;
   108   }
   110   NS_IMETHODIMP
   111   GetClassIDNoAlloc(nsCID *_cid)
   112   {
   113     return NS_ERROR_NOT_AVAILABLE;
   114   }
   115 };
   117 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() { return 2; }
   118 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() { return 1; }
   119 NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo)
   121 static StatementClassInfo sStatementClassInfo;
   123 ////////////////////////////////////////////////////////////////////////////////
   124 //// Statement
   126 Statement::Statement()
   127 : StorageBaseStatementInternal()
   128 , mDBStatement(nullptr)
   129 , mColumnNames()
   130 , mExecuting(false)
   131 {
   132 }
   134 nsresult
   135 Statement::initialize(Connection *aDBConnection,
   136                       sqlite3 *aNativeConnection,
   137                       const nsACString &aSQLStatement)
   138 {
   139   MOZ_ASSERT(aDBConnection, "No database connection given!");
   140   MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
   141   MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
   142   MOZ_ASSERT(aNativeConnection, "No native connection given!");
   144   int srv = aDBConnection->prepareStatement(aNativeConnection,
   145                                             PromiseFlatCString(aSQLStatement),
   146                                             &mDBStatement);
   147   if (srv != SQLITE_OK) {
   148       PR_LOG(gStorageLog, PR_LOG_ERROR,
   149              ("Sqlite statement prepare error: %d '%s'", srv,
   150               ::sqlite3_errmsg(aNativeConnection)));
   151       PR_LOG(gStorageLog, PR_LOG_ERROR,
   152              ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
   153       return NS_ERROR_FAILURE;
   154     }
   156   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
   157                                       PromiseFlatCString(aSQLStatement).get(),
   158                                       mDBStatement));
   160   mDBConnection = aDBConnection;
   161   mNativeConnection = aNativeConnection;
   162   mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
   163   mResultColumnCount = ::sqlite3_column_count(mDBStatement);
   164   mColumnNames.Clear();
   166   for (uint32_t i = 0; i < mResultColumnCount; i++) {
   167       const char *name = ::sqlite3_column_name(mDBStatement, i);
   168       (void)mColumnNames.AppendElement(nsDependentCString(name));
   169   }
   171 #ifdef DEBUG
   172   // We want to try and test for LIKE and that consumers are using
   173   // escapeStringForLIKE instead of just trusting user input.  The idea to
   174   // check to see if they are binding a parameter after like instead of just
   175   // using a string.  We only do this in debug builds because it's expensive!
   176   const nsCaseInsensitiveCStringComparator c;
   177   nsACString::const_iterator start, end, e;
   178   aSQLStatement.BeginReading(start);
   179   aSQLStatement.EndReading(end);
   180   e = end;
   181   while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
   182     // We have a LIKE in here, so we perform our tests
   183     // FindInReadable moves the iterator, so we have to get a new one for
   184     // each test we perform.
   185     nsACString::const_iterator s1, s2, s3;
   186     s1 = s2 = s3 = start;
   188     if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
   189           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
   190           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
   191       // At this point, we didn't find a LIKE statement followed by ?, :,
   192       // or @, all of which are valid characters for binding a parameter.
   193       // We will warn the consumer that they may not be safely using LIKE.
   194       NS_WARNING("Unsafe use of LIKE detected!  Please ensure that you "
   195                  "are using mozIStorageStatement::escapeStringForLIKE "
   196                  "and that you are binding that result to the statement "
   197                  "to prevent SQL injection attacks.");
   198     }
   200     // resetting start and e
   201     start = e;
   202     e = end;
   203   }
   204 #endif
   206   return NS_OK;
   207 }
   209 mozIStorageBindingParams *
   210 Statement::getParams()
   211 {
   212   nsresult rv;
   214   // If we do not have an array object yet, make it.
   215   if (!mParamsArray) {
   216     nsCOMPtr<mozIStorageBindingParamsArray> array;
   217     rv = NewBindingParamsArray(getter_AddRefs(array));
   218     NS_ENSURE_SUCCESS(rv, nullptr);
   220     mParamsArray = static_cast<BindingParamsArray *>(array.get());
   221   }
   223   // If there isn't already any rows added, we'll have to add one to use.
   224   if (mParamsArray->length() == 0) {
   225     nsRefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
   226     NS_ENSURE_TRUE(params, nullptr);
   228     rv = mParamsArray->AddParams(params);
   229     NS_ENSURE_SUCCESS(rv, nullptr);
   231     // We have to unlock our params because AddParams locks them.  This is safe
   232     // because no reference to the params object was, or ever will be given out.
   233     params->unlock(this);
   235     // We also want to lock our array at this point - we don't want anything to
   236     // be added to it.  Nothing has, or will ever get a reference to it, but we
   237     // will get additional safety checks via assertions by doing this.
   238     mParamsArray->lock();
   239   }
   241   return *mParamsArray->begin();
   242 }
   244 Statement::~Statement()
   245 {
   246   (void)internalFinalize(true);
   247 }
   249 ////////////////////////////////////////////////////////////////////////////////
   250 //// nsISupports
   252 NS_IMPL_ADDREF(Statement)
   253 NS_IMPL_RELEASE(Statement)
   255 NS_INTERFACE_MAP_BEGIN(Statement)
   256   NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
   257   NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
   258   NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
   259   NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
   260   NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
   261   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
   262     foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo);
   263   }
   264   else
   265   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
   266 NS_INTERFACE_MAP_END
   269 ////////////////////////////////////////////////////////////////////////////////
   270 //// StorageBaseStatementInternal
   272 Connection *
   273 Statement::getOwner()
   274 {
   275   return mDBConnection;
   276 }
   278 int
   279 Statement::getAsyncStatement(sqlite3_stmt **_stmt)
   280 {
   281   // If we have no statement, we shouldn't be calling this method!
   282   NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
   284   // If we do not yet have a cached async statement, clone our statement now.
   285   if (!mAsyncStatement) {
   286     nsDependentCString sql(::sqlite3_sql(mDBStatement));
   287     int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
   288                                              &mAsyncStatement);
   289     if (rc != SQLITE_OK) {
   290       *_stmt = nullptr;
   291       return rc;
   292     }
   294     PR_LOG(gStorageLog, PR_LOG_NOTICE,
   295            ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
   296   }
   298   *_stmt = mAsyncStatement;
   299   return SQLITE_OK;
   300 }
   302 nsresult
   303 Statement::getAsynchronousStatementData(StatementData &_data)
   304 {
   305   if (!mDBStatement)
   306     return NS_ERROR_UNEXPECTED;
   308   sqlite3_stmt *stmt;
   309   int rc = getAsyncStatement(&stmt);
   310   if (rc != SQLITE_OK)
   311     return convertResultCode(rc);
   313   _data = StatementData(stmt, bindingParamsArray(), this);
   315   return NS_OK;
   316 }
   318 already_AddRefed<mozIStorageBindingParams>
   319 Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
   320 {
   321   nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
   322   return params.forget();
   323 }
   326 ////////////////////////////////////////////////////////////////////////////////
   327 //// mozIStorageStatement
   329 // proxy to StorageBaseStatementInternal using its define helper.
   330 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
   332 NS_IMETHODIMP
   333 Statement::Clone(mozIStorageStatement **_statement)
   334 {
   335   nsRefPtr<Statement> statement(new Statement());
   336   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
   338   nsAutoCString sql(::sqlite3_sql(mDBStatement));
   339   nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
   340   NS_ENSURE_SUCCESS(rv, rv);
   342   statement.forget(_statement);
   343   return NS_OK;
   344 }
   346 NS_IMETHODIMP
   347 Statement::Finalize()
   348 {
   349   return internalFinalize(false);
   350 }
   352 nsresult
   353 Statement::internalFinalize(bool aDestructing)
   354 {
   355   if (!mDBStatement)
   356     return NS_OK;
   358   int srv = SQLITE_OK;
   360   if (!mDBConnection->isClosed()) {
   361     //
   362     // The connection is still open. While statement finalization and
   363     // closing may, in some cases, take place in two distinct threads,
   364     // we have a guarantee that the connection will remain open until
   365     // this method terminates:
   366     //
   367     // a. The connection will be closed synchronously. In this case,
   368     // there is no race condition, as everything takes place on the
   369     // same thread.
   370     //
   371     // b. The connection is closed asynchronously and this code is
   372     // executed on the opener thread. In this case, asyncClose() has
   373     // not been called yet and will not be called before we return
   374     // from this function.
   375     //
   376     // c. The connection is closed asynchronously and this code is
   377     // executed on the async execution thread. In this case,
   378     // AsyncCloseConnection::Run() has not been called yet and will
   379     // not be called before we return from this function.
   380     //
   381     // In either case, the connection is still valid, hence closing
   382     // here is safe.
   383     //
   384     PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s' during garbage-collection",
   385                                         ::sqlite3_sql(mDBStatement)));
   386     srv = ::sqlite3_finalize(mDBStatement);
   387   }
   388 #ifdef DEBUG
   389   else {
   390     //
   391     // The database connection is either closed or closing. The sqlite
   392     // statement has either been finalized already by the connection
   393     // or is about to be finalized by the connection.
   394     //
   395     // Finalizing it here would be useless and segfaultish.
   396     //
   398     char *msg = ::PR_smprintf("SQL statement (%x) should have been finalized"
   399       " before garbage-collection. For more details on this statement, set"
   400       " NSPR_LOG_MESSAGES=mozStorage:5 .",
   401       mDBStatement);
   403     //
   404     // Note that we can't display the statement itself, as the data structure
   405     // is not valid anymore. However, the address shown here should help
   406     // developers correlate with the more complete debug message triggered
   407     // by AsyncClose().
   408     //
   410 #if 0
   411     // Deactivate the warning until we have fixed the exising culprit
   412     // (see bug 914070).
   413     NS_WARNING(msg);
   414 #endif // 0
   416     PR_LOG(gStorageLog, PR_LOG_WARNING, (msg));
   418     ::PR_smprintf_free(msg);
   419   }
   421 #endif
   423   mDBStatement = nullptr;
   425   if (mAsyncStatement) {
   426     // If the destructor called us, there are no pending async statements (they
   427     // hold a reference to us) and we can/must just kill the statement directly.
   428     if (aDestructing)
   429       destructorAsyncFinalize();
   430     else
   431       asyncFinalize();
   432   }
   434   // We are considered dead at this point, so any wrappers for row or params
   435   // need to lose their reference to us.
   436   if (mStatementParamsHolder) {
   437     nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
   438         do_QueryInterface(mStatementParamsHolder);
   439     nsCOMPtr<mozIStorageStatementParams> iParams =
   440         do_QueryWrappedNative(wrapper);
   441     StatementParams *params = static_cast<StatementParams *>(iParams.get());
   442     params->mStatement = nullptr;
   443     mStatementParamsHolder = nullptr;
   444   }
   446   if (mStatementRowHolder) {
   447     nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
   448         do_QueryInterface(mStatementRowHolder);
   449     nsCOMPtr<mozIStorageStatementRow> iRow =
   450         do_QueryWrappedNative(wrapper);
   451     StatementRow *row = static_cast<StatementRow *>(iRow.get());
   452     row->mStatement = nullptr;
   453     mStatementRowHolder = nullptr;
   454   }
   456   return convertResultCode(srv);
   457 }
   459 NS_IMETHODIMP
   460 Statement::GetParameterCount(uint32_t *_parameterCount)
   461 {
   462   if (!mDBStatement)
   463     return NS_ERROR_NOT_INITIALIZED;
   465   *_parameterCount = mParamCount;
   466   return NS_OK;
   467 }
   469 NS_IMETHODIMP
   470 Statement::GetParameterName(uint32_t aParamIndex,
   471                             nsACString &_name)
   472 {
   473   if (!mDBStatement)
   474     return NS_ERROR_NOT_INITIALIZED;
   475   ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
   477   const char *name = ::sqlite3_bind_parameter_name(mDBStatement,
   478                                                    aParamIndex + 1);
   479   if (name == nullptr) {
   480     // this thing had no name, so fake one
   481     nsAutoCString name(":");
   482     name.AppendInt(aParamIndex);
   483     _name.Assign(name);
   484   }
   485   else {
   486     _name.Assign(nsDependentCString(name));
   487   }
   489   return NS_OK;
   490 }
   492 NS_IMETHODIMP
   493 Statement::GetParameterIndex(const nsACString &aName,
   494                              uint32_t *_index)
   495 {
   496   if (!mDBStatement)
   497     return NS_ERROR_NOT_INITIALIZED;
   499   // We do not accept any forms of names other than ":name", but we need to add
   500   // the colon for SQLite.
   501   nsAutoCString name(":");
   502   name.Append(aName);
   503   int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
   504   if (ind  == 0) // Named parameter not found.
   505     return NS_ERROR_INVALID_ARG;
   507   *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
   509   return NS_OK;
   510 }
   512 NS_IMETHODIMP
   513 Statement::GetColumnCount(uint32_t *_columnCount)
   514 {
   515   if (!mDBStatement)
   516     return NS_ERROR_NOT_INITIALIZED;
   518   *_columnCount = mResultColumnCount;
   519   return NS_OK;
   520 }
   522 NS_IMETHODIMP
   523 Statement::GetColumnName(uint32_t aColumnIndex,
   524                          nsACString &_name)
   525 {
   526   if (!mDBStatement)
   527     return NS_ERROR_NOT_INITIALIZED;
   528   ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
   530   const char *cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
   531   _name.Assign(nsDependentCString(cname));
   533   return NS_OK;
   534 }
   536 NS_IMETHODIMP
   537 Statement::GetColumnIndex(const nsACString &aName,
   538                           uint32_t *_index)
   539 {
   540   if (!mDBStatement)
   541       return NS_ERROR_NOT_INITIALIZED;
   543   // Surprisingly enough, SQLite doesn't provide an API for this.  We have to
   544   // determine it ourselves sadly.
   545   for (uint32_t i = 0; i < mResultColumnCount; i++) {
   546     if (mColumnNames[i].Equals(aName)) {
   547       *_index = i;
   548       return NS_OK;
   549     }
   550   }
   552   return NS_ERROR_INVALID_ARG;
   553 }
   555 NS_IMETHODIMP
   556 Statement::Reset()
   557 {
   558   if (!mDBStatement)
   559     return NS_ERROR_NOT_INITIALIZED;
   561 #ifdef DEBUG
   562   PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Resetting statement: '%s'",
   563                                      ::sqlite3_sql(mDBStatement)));
   565   checkAndLogStatementPerformance(mDBStatement);
   566 #endif
   568   mParamsArray = nullptr;
   569   (void)sqlite3_reset(mDBStatement);
   570   (void)sqlite3_clear_bindings(mDBStatement);
   572   mExecuting = false;
   574   return NS_OK;
   575 }
   577 NS_IMETHODIMP
   578 Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
   579 {
   580   if (!mDBStatement)
   581     return NS_ERROR_NOT_INITIALIZED;
   583   BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
   584   if (array->getOwner() != this)
   585     return NS_ERROR_UNEXPECTED;
   587   if (array->length() == 0)
   588     return NS_ERROR_UNEXPECTED;
   590   mParamsArray = array;
   591   mParamsArray->lock();
   593   return NS_OK;
   594 }
   596 NS_IMETHODIMP
   597 Statement::Execute()
   598 {
   599   if (!mDBStatement)
   600     return NS_ERROR_NOT_INITIALIZED;
   602   bool ret;
   603   nsresult rv = ExecuteStep(&ret);
   604   nsresult rv2 = Reset();
   606   return NS_FAILED(rv) ? rv : rv2;
   607 }
   609 NS_IMETHODIMP
   610 Statement::ExecuteStep(bool *_moreResults)
   611 {
   612   PROFILER_LABEL("storage", "Statement::ExecuteStep");
   613   if (!mDBStatement)
   614     return NS_ERROR_NOT_INITIALIZED;
   616   // Bind any parameters first before executing.
   617   if (mParamsArray) {
   618     // If we have more than one row of parameters to bind, they shouldn't be
   619     // calling this method (and instead use executeAsync).
   620     if (mParamsArray->length() != 1)
   621       return NS_ERROR_UNEXPECTED;
   623     BindingParamsArray::iterator row = mParamsArray->begin();
   624     nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = 
   625       do_QueryInterface(*row);
   626     nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
   627     if (error) {
   628       int32_t srv;
   629       (void)error->GetResult(&srv);
   630       return convertResultCode(srv);
   631     }
   633     // We have bound, so now we can clear our array.
   634     mParamsArray = nullptr;
   635   }
   636   int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
   638 #ifdef PR_LOGGING
   639   if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
   640       nsAutoCString errStr;
   641       (void)mDBConnection->GetLastErrorString(errStr);
   642       PR_LOG(gStorageLog, PR_LOG_DEBUG,
   643              ("Statement::ExecuteStep error: %s", errStr.get()));
   644   }
   645 #endif
   647   // SQLITE_ROW and SQLITE_DONE are non-errors
   648   if (srv == SQLITE_ROW) {
   649     // we got a row back
   650     mExecuting = true;
   651     *_moreResults = true;
   652     return NS_OK;
   653   }
   654   else if (srv == SQLITE_DONE) {
   655     // statement is done (no row returned)
   656     mExecuting = false;
   657     *_moreResults = false;
   658     return NS_OK;
   659   }
   660   else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
   661     mExecuting = false;
   662   }
   663   else if (mExecuting) {
   664     PR_LOG(gStorageLog, PR_LOG_ERROR,
   665            ("SQLite error after mExecuting was true!"));
   666     mExecuting = false;
   667   }
   669   return convertResultCode(srv);
   670 }
   672 NS_IMETHODIMP
   673 Statement::GetState(int32_t *_state)
   674 {
   675   if (!mDBStatement)
   676     *_state = MOZ_STORAGE_STATEMENT_INVALID;
   677   else if (mExecuting)
   678     *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
   679   else
   680     *_state = MOZ_STORAGE_STATEMENT_READY;
   682   return NS_OK;
   683 }
   685 NS_IMETHODIMP
   686 Statement::GetColumnDecltype(uint32_t aParamIndex,
   687                              nsACString &_declType)
   688 {
   689   if (!mDBStatement)
   690     return NS_ERROR_NOT_INITIALIZED;
   692   ENSURE_INDEX_VALUE(aParamIndex, mResultColumnCount);
   694   _declType.Assign(::sqlite3_column_decltype(mDBStatement, aParamIndex));
   695   return NS_OK;
   696 }
   698 ////////////////////////////////////////////////////////////////////////////////
   699 //// mozIStorageValueArray (now part of mozIStorageStatement too)
   701 NS_IMETHODIMP
   702 Statement::GetNumEntries(uint32_t *_length)
   703 {
   704   *_length = mResultColumnCount;
   705   return NS_OK;
   706 }
   708 NS_IMETHODIMP
   709 Statement::GetTypeOfIndex(uint32_t aIndex,
   710                           int32_t *_type)
   711 {
   712   if (!mDBStatement)
   713     return NS_ERROR_NOT_INITIALIZED;
   715   ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   717   if (!mExecuting)
   718     return NS_ERROR_UNEXPECTED;
   720   int t = ::sqlite3_column_type(mDBStatement, aIndex);
   721   switch (t) {
   722     case SQLITE_INTEGER:
   723       *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
   724       break;
   725     case SQLITE_FLOAT:
   726       *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
   727       break;
   728     case SQLITE_TEXT:
   729       *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
   730       break;
   731     case SQLITE_BLOB:
   732       *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
   733       break;
   734     case SQLITE_NULL:
   735       *_type = mozIStorageStatement::VALUE_TYPE_NULL;
   736       break;
   737     default:
   738       return NS_ERROR_FAILURE;
   739   }
   741   return NS_OK;
   742 }
   744 NS_IMETHODIMP
   745 Statement::GetInt32(uint32_t aIndex,
   746                     int32_t *_value)
   747 {
   748   if (!mDBStatement)
   749     return NS_ERROR_NOT_INITIALIZED;
   751   ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   753   if (!mExecuting)
   754     return NS_ERROR_UNEXPECTED;
   756   *_value = ::sqlite3_column_int(mDBStatement, aIndex);
   757   return NS_OK;
   758 }
   760 NS_IMETHODIMP
   761 Statement::GetInt64(uint32_t aIndex,
   762                     int64_t *_value)
   763 {
   764   if (!mDBStatement)
   765     return NS_ERROR_NOT_INITIALIZED;
   767   ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   769   if (!mExecuting)
   770     return NS_ERROR_UNEXPECTED;
   772   *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
   774   return NS_OK;
   775 }
   777 NS_IMETHODIMP
   778 Statement::GetDouble(uint32_t aIndex,
   779                      double *_value)
   780 {
   781   if (!mDBStatement)
   782     return NS_ERROR_NOT_INITIALIZED;
   784   ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   786   if (!mExecuting)
   787     return NS_ERROR_UNEXPECTED;
   789   *_value = ::sqlite3_column_double(mDBStatement, aIndex);
   791   return NS_OK;
   792 }
   794 NS_IMETHODIMP
   795 Statement::GetUTF8String(uint32_t aIndex,
   796                          nsACString &_value)
   797 {
   798   // Get type of Index will check aIndex for us, so we don't have to.
   799   int32_t type;
   800   nsresult rv = GetTypeOfIndex(aIndex, &type);
   801   NS_ENSURE_SUCCESS(rv, rv);
   802   if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
   803     // NULL columns should have IsVoid set to distinguish them from the empty
   804     // string.
   805     _value.Truncate(0);
   806     _value.SetIsVoid(true);
   807   }
   808   else {
   809     const char *value =
   810       reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
   811                                                            aIndex));
   812     _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
   813   }
   814   return NS_OK;
   815 }
   817 NS_IMETHODIMP
   818 Statement::GetString(uint32_t aIndex,
   819                      nsAString &_value)
   820 {
   821   // Get type of Index will check aIndex for us, so we don't have to.
   822   int32_t type;
   823   nsresult rv = GetTypeOfIndex(aIndex, &type);
   824   NS_ENSURE_SUCCESS(rv, rv);
   825   if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
   826     // NULL columns should have IsVoid set to distinguish them from the empty
   827     // string.
   828     _value.Truncate(0);
   829     _value.SetIsVoid(true);
   830   } else {
   831     const char16_t *value =
   832       static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
   833                                                              aIndex));
   834     _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / 2);
   835   }
   836   return NS_OK;
   837 }
   839 NS_IMETHODIMP
   840 Statement::GetBlob(uint32_t aIndex,
   841                    uint32_t *_size,
   842                    uint8_t **_blob)
   843 {
   844   if (!mDBStatement)
   845     return NS_ERROR_NOT_INITIALIZED;
   847   ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   849   if (!mExecuting)
   850      return NS_ERROR_UNEXPECTED;
   852   int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
   853   void *blob = nullptr;
   854   if (size) {
   855     blob = nsMemory::Clone(::sqlite3_column_blob(mDBStatement, aIndex), size);
   856     NS_ENSURE_TRUE(blob, NS_ERROR_OUT_OF_MEMORY);
   857   }
   859   *_blob = static_cast<uint8_t *>(blob);
   860   *_size = size;
   861   return NS_OK;
   862 }
   864 NS_IMETHODIMP
   865 Statement::GetSharedUTF8String(uint32_t aIndex,
   866                                uint32_t *_length,
   867                                const char **_value)
   868 {
   869   if (_length)
   870     *_length = ::sqlite3_column_bytes(mDBStatement, aIndex);
   872   *_value = reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
   873                                                                  aIndex));
   874   return NS_OK;
   875 }
   877 NS_IMETHODIMP
   878 Statement::GetSharedString(uint32_t aIndex,
   879                            uint32_t *_length,
   880                            const char16_t **_value)
   881 {
   882   if (_length)
   883     *_length = ::sqlite3_column_bytes16(mDBStatement, aIndex);
   885   *_value = static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
   886                                                                    aIndex));
   887   return NS_OK;
   888 }
   890 NS_IMETHODIMP
   891 Statement::GetSharedBlob(uint32_t aIndex,
   892                          uint32_t *_size,
   893                          const uint8_t **_blob)
   894 {
   895   *_size = ::sqlite3_column_bytes(mDBStatement, aIndex);
   896   *_blob = static_cast<const uint8_t *>(::sqlite3_column_blob(mDBStatement,
   897                                                               aIndex));
   898   return NS_OK;
   899 }
   901 NS_IMETHODIMP
   902 Statement::GetIsNull(uint32_t aIndex,
   903                      bool *_isNull)
   904 {
   905   // Get type of Index will check aIndex for us, so we don't have to.
   906   int32_t type;
   907   nsresult rv = GetTypeOfIndex(aIndex, &type);
   908   NS_ENSURE_SUCCESS(rv, rv);
   909   *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
   910   return NS_OK;
   911 }
   913 ////////////////////////////////////////////////////////////////////////////////
   914 //// mozIStorageBindingParams
   916 BOILERPLATE_BIND_PROXIES(
   917   Statement, 
   918   if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
   919 )
   921 } // namespace storage
   922 } // namespace mozilla

mercurial