1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/src/mozStorageStatement.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,922 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <limits.h> 1.11 +#include <stdio.h> 1.12 + 1.13 +#include "nsError.h" 1.14 +#include "nsMemory.h" 1.15 +#include "nsThreadUtils.h" 1.16 +#include "nsIClassInfoImpl.h" 1.17 +#include "nsIProgrammingLanguage.h" 1.18 +#include "Variant.h" 1.19 + 1.20 +#include "mozIStorageError.h" 1.21 + 1.22 +#include "mozStorageBindingParams.h" 1.23 +#include "mozStorageConnection.h" 1.24 +#include "mozStorageStatementJSHelper.h" 1.25 +#include "mozStoragePrivateHelpers.h" 1.26 +#include "mozStorageStatementParams.h" 1.27 +#include "mozStorageStatementRow.h" 1.28 +#include "mozStorageStatement.h" 1.29 +#include "GeckoProfiler.h" 1.30 +#include "nsDOMClassInfo.h" 1.31 + 1.32 +#include "prlog.h" 1.33 + 1.34 + 1.35 +#ifdef PR_LOGGING 1.36 +extern PRLogModuleInfo* gStorageLog; 1.37 +#endif 1.38 + 1.39 +namespace mozilla { 1.40 +namespace storage { 1.41 + 1.42 +//////////////////////////////////////////////////////////////////////////////// 1.43 +//// nsIClassInfo 1.44 + 1.45 +NS_IMPL_CI_INTERFACE_GETTER(Statement, 1.46 + mozIStorageStatement, 1.47 + mozIStorageBaseStatement, 1.48 + mozIStorageBindingParams, 1.49 + mozIStorageValueArray, 1.50 + mozilla::storage::StorageBaseStatementInternal) 1.51 + 1.52 +class StatementClassInfo : public nsIClassInfo 1.53 +{ 1.54 +public: 1.55 + MOZ_CONSTEXPR StatementClassInfo() {} 1.56 + 1.57 + NS_DECL_ISUPPORTS_INHERITED 1.58 + 1.59 + NS_IMETHODIMP 1.60 + GetInterfaces(uint32_t *_count, nsIID ***_array) 1.61 + { 1.62 + return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array); 1.63 + } 1.64 + 1.65 + NS_IMETHODIMP 1.66 + GetHelperForLanguage(uint32_t aLanguage, nsISupports **_helper) 1.67 + { 1.68 + if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) { 1.69 + static StatementJSHelper sJSHelper; 1.70 + *_helper = &sJSHelper; 1.71 + return NS_OK; 1.72 + } 1.73 + 1.74 + *_helper = nullptr; 1.75 + return NS_OK; 1.76 + } 1.77 + 1.78 + NS_IMETHODIMP 1.79 + GetContractID(char **_contractID) 1.80 + { 1.81 + *_contractID = nullptr; 1.82 + return NS_OK; 1.83 + } 1.84 + 1.85 + NS_IMETHODIMP 1.86 + GetClassDescription(char **_desc) 1.87 + { 1.88 + *_desc = nullptr; 1.89 + return NS_OK; 1.90 + } 1.91 + 1.92 + NS_IMETHODIMP 1.93 + GetClassID(nsCID **_id) 1.94 + { 1.95 + *_id = nullptr; 1.96 + return NS_OK; 1.97 + } 1.98 + 1.99 + NS_IMETHODIMP 1.100 + GetImplementationLanguage(uint32_t *_language) 1.101 + { 1.102 + *_language = nsIProgrammingLanguage::CPLUSPLUS; 1.103 + return NS_OK; 1.104 + } 1.105 + 1.106 + NS_IMETHODIMP 1.107 + GetFlags(uint32_t *_flags) 1.108 + { 1.109 + *_flags = 0; 1.110 + return NS_OK; 1.111 + } 1.112 + 1.113 + NS_IMETHODIMP 1.114 + GetClassIDNoAlloc(nsCID *_cid) 1.115 + { 1.116 + return NS_ERROR_NOT_AVAILABLE; 1.117 + } 1.118 +}; 1.119 + 1.120 +NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() { return 2; } 1.121 +NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() { return 1; } 1.122 +NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo) 1.123 + 1.124 +static StatementClassInfo sStatementClassInfo; 1.125 + 1.126 +//////////////////////////////////////////////////////////////////////////////// 1.127 +//// Statement 1.128 + 1.129 +Statement::Statement() 1.130 +: StorageBaseStatementInternal() 1.131 +, mDBStatement(nullptr) 1.132 +, mColumnNames() 1.133 +, mExecuting(false) 1.134 +{ 1.135 +} 1.136 + 1.137 +nsresult 1.138 +Statement::initialize(Connection *aDBConnection, 1.139 + sqlite3 *aNativeConnection, 1.140 + const nsACString &aSQLStatement) 1.141 +{ 1.142 + MOZ_ASSERT(aDBConnection, "No database connection given!"); 1.143 + MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid"); 1.144 + MOZ_ASSERT(!mDBStatement, "Statement already initialized!"); 1.145 + MOZ_ASSERT(aNativeConnection, "No native connection given!"); 1.146 + 1.147 + int srv = aDBConnection->prepareStatement(aNativeConnection, 1.148 + PromiseFlatCString(aSQLStatement), 1.149 + &mDBStatement); 1.150 + if (srv != SQLITE_OK) { 1.151 + PR_LOG(gStorageLog, PR_LOG_ERROR, 1.152 + ("Sqlite statement prepare error: %d '%s'", srv, 1.153 + ::sqlite3_errmsg(aNativeConnection))); 1.154 + PR_LOG(gStorageLog, PR_LOG_ERROR, 1.155 + ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get())); 1.156 + return NS_ERROR_FAILURE; 1.157 + } 1.158 + 1.159 + PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)", 1.160 + PromiseFlatCString(aSQLStatement).get(), 1.161 + mDBStatement)); 1.162 + 1.163 + mDBConnection = aDBConnection; 1.164 + mNativeConnection = aNativeConnection; 1.165 + mParamCount = ::sqlite3_bind_parameter_count(mDBStatement); 1.166 + mResultColumnCount = ::sqlite3_column_count(mDBStatement); 1.167 + mColumnNames.Clear(); 1.168 + 1.169 + for (uint32_t i = 0; i < mResultColumnCount; i++) { 1.170 + const char *name = ::sqlite3_column_name(mDBStatement, i); 1.171 + (void)mColumnNames.AppendElement(nsDependentCString(name)); 1.172 + } 1.173 + 1.174 +#ifdef DEBUG 1.175 + // We want to try and test for LIKE and that consumers are using 1.176 + // escapeStringForLIKE instead of just trusting user input. The idea to 1.177 + // check to see if they are binding a parameter after like instead of just 1.178 + // using a string. We only do this in debug builds because it's expensive! 1.179 + const nsCaseInsensitiveCStringComparator c; 1.180 + nsACString::const_iterator start, end, e; 1.181 + aSQLStatement.BeginReading(start); 1.182 + aSQLStatement.EndReading(end); 1.183 + e = end; 1.184 + while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) { 1.185 + // We have a LIKE in here, so we perform our tests 1.186 + // FindInReadable moves the iterator, so we have to get a new one for 1.187 + // each test we perform. 1.188 + nsACString::const_iterator s1, s2, s3; 1.189 + s1 = s2 = s3 = start; 1.190 + 1.191 + if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) || 1.192 + ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) || 1.193 + ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) { 1.194 + // At this point, we didn't find a LIKE statement followed by ?, :, 1.195 + // or @, all of which are valid characters for binding a parameter. 1.196 + // We will warn the consumer that they may not be safely using LIKE. 1.197 + NS_WARNING("Unsafe use of LIKE detected! Please ensure that you " 1.198 + "are using mozIStorageStatement::escapeStringForLIKE " 1.199 + "and that you are binding that result to the statement " 1.200 + "to prevent SQL injection attacks."); 1.201 + } 1.202 + 1.203 + // resetting start and e 1.204 + start = e; 1.205 + e = end; 1.206 + } 1.207 +#endif 1.208 + 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +mozIStorageBindingParams * 1.213 +Statement::getParams() 1.214 +{ 1.215 + nsresult rv; 1.216 + 1.217 + // If we do not have an array object yet, make it. 1.218 + if (!mParamsArray) { 1.219 + nsCOMPtr<mozIStorageBindingParamsArray> array; 1.220 + rv = NewBindingParamsArray(getter_AddRefs(array)); 1.221 + NS_ENSURE_SUCCESS(rv, nullptr); 1.222 + 1.223 + mParamsArray = static_cast<BindingParamsArray *>(array.get()); 1.224 + } 1.225 + 1.226 + // If there isn't already any rows added, we'll have to add one to use. 1.227 + if (mParamsArray->length() == 0) { 1.228 + nsRefPtr<BindingParams> params(new BindingParams(mParamsArray, this)); 1.229 + NS_ENSURE_TRUE(params, nullptr); 1.230 + 1.231 + rv = mParamsArray->AddParams(params); 1.232 + NS_ENSURE_SUCCESS(rv, nullptr); 1.233 + 1.234 + // We have to unlock our params because AddParams locks them. This is safe 1.235 + // because no reference to the params object was, or ever will be given out. 1.236 + params->unlock(this); 1.237 + 1.238 + // We also want to lock our array at this point - we don't want anything to 1.239 + // be added to it. Nothing has, or will ever get a reference to it, but we 1.240 + // will get additional safety checks via assertions by doing this. 1.241 + mParamsArray->lock(); 1.242 + } 1.243 + 1.244 + return *mParamsArray->begin(); 1.245 +} 1.246 + 1.247 +Statement::~Statement() 1.248 +{ 1.249 + (void)internalFinalize(true); 1.250 +} 1.251 + 1.252 +//////////////////////////////////////////////////////////////////////////////// 1.253 +//// nsISupports 1.254 + 1.255 +NS_IMPL_ADDREF(Statement) 1.256 +NS_IMPL_RELEASE(Statement) 1.257 + 1.258 +NS_INTERFACE_MAP_BEGIN(Statement) 1.259 + NS_INTERFACE_MAP_ENTRY(mozIStorageStatement) 1.260 + NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement) 1.261 + NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams) 1.262 + NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray) 1.263 + NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal) 1.264 + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { 1.265 + foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo); 1.266 + } 1.267 + else 1.268 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement) 1.269 +NS_INTERFACE_MAP_END 1.270 + 1.271 + 1.272 +//////////////////////////////////////////////////////////////////////////////// 1.273 +//// StorageBaseStatementInternal 1.274 + 1.275 +Connection * 1.276 +Statement::getOwner() 1.277 +{ 1.278 + return mDBConnection; 1.279 +} 1.280 + 1.281 +int 1.282 +Statement::getAsyncStatement(sqlite3_stmt **_stmt) 1.283 +{ 1.284 + // If we have no statement, we shouldn't be calling this method! 1.285 + NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!"); 1.286 + 1.287 + // If we do not yet have a cached async statement, clone our statement now. 1.288 + if (!mAsyncStatement) { 1.289 + nsDependentCString sql(::sqlite3_sql(mDBStatement)); 1.290 + int rc = mDBConnection->prepareStatement(mNativeConnection, sql, 1.291 + &mAsyncStatement); 1.292 + if (rc != SQLITE_OK) { 1.293 + *_stmt = nullptr; 1.294 + return rc; 1.295 + } 1.296 + 1.297 + PR_LOG(gStorageLog, PR_LOG_NOTICE, 1.298 + ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement)); 1.299 + } 1.300 + 1.301 + *_stmt = mAsyncStatement; 1.302 + return SQLITE_OK; 1.303 +} 1.304 + 1.305 +nsresult 1.306 +Statement::getAsynchronousStatementData(StatementData &_data) 1.307 +{ 1.308 + if (!mDBStatement) 1.309 + return NS_ERROR_UNEXPECTED; 1.310 + 1.311 + sqlite3_stmt *stmt; 1.312 + int rc = getAsyncStatement(&stmt); 1.313 + if (rc != SQLITE_OK) 1.314 + return convertResultCode(rc); 1.315 + 1.316 + _data = StatementData(stmt, bindingParamsArray(), this); 1.317 + 1.318 + return NS_OK; 1.319 +} 1.320 + 1.321 +already_AddRefed<mozIStorageBindingParams> 1.322 +Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner) 1.323 +{ 1.324 + nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this); 1.325 + return params.forget(); 1.326 +} 1.327 + 1.328 + 1.329 +//////////////////////////////////////////////////////////////////////////////// 1.330 +//// mozIStorageStatement 1.331 + 1.332 +// proxy to StorageBaseStatementInternal using its define helper. 1.333 +MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;) 1.334 + 1.335 +NS_IMETHODIMP 1.336 +Statement::Clone(mozIStorageStatement **_statement) 1.337 +{ 1.338 + nsRefPtr<Statement> statement(new Statement()); 1.339 + NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY); 1.340 + 1.341 + nsAutoCString sql(::sqlite3_sql(mDBStatement)); 1.342 + nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql); 1.343 + NS_ENSURE_SUCCESS(rv, rv); 1.344 + 1.345 + statement.forget(_statement); 1.346 + return NS_OK; 1.347 +} 1.348 + 1.349 +NS_IMETHODIMP 1.350 +Statement::Finalize() 1.351 +{ 1.352 + return internalFinalize(false); 1.353 +} 1.354 + 1.355 +nsresult 1.356 +Statement::internalFinalize(bool aDestructing) 1.357 +{ 1.358 + if (!mDBStatement) 1.359 + return NS_OK; 1.360 + 1.361 + int srv = SQLITE_OK; 1.362 + 1.363 + if (!mDBConnection->isClosed()) { 1.364 + // 1.365 + // The connection is still open. While statement finalization and 1.366 + // closing may, in some cases, take place in two distinct threads, 1.367 + // we have a guarantee that the connection will remain open until 1.368 + // this method terminates: 1.369 + // 1.370 + // a. The connection will be closed synchronously. In this case, 1.371 + // there is no race condition, as everything takes place on the 1.372 + // same thread. 1.373 + // 1.374 + // b. The connection is closed asynchronously and this code is 1.375 + // executed on the opener thread. In this case, asyncClose() has 1.376 + // not been called yet and will not be called before we return 1.377 + // from this function. 1.378 + // 1.379 + // c. The connection is closed asynchronously and this code is 1.380 + // executed on the async execution thread. In this case, 1.381 + // AsyncCloseConnection::Run() has not been called yet and will 1.382 + // not be called before we return from this function. 1.383 + // 1.384 + // In either case, the connection is still valid, hence closing 1.385 + // here is safe. 1.386 + // 1.387 + PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s' during garbage-collection", 1.388 + ::sqlite3_sql(mDBStatement))); 1.389 + srv = ::sqlite3_finalize(mDBStatement); 1.390 + } 1.391 +#ifdef DEBUG 1.392 + else { 1.393 + // 1.394 + // The database connection is either closed or closing. The sqlite 1.395 + // statement has either been finalized already by the connection 1.396 + // or is about to be finalized by the connection. 1.397 + // 1.398 + // Finalizing it here would be useless and segfaultish. 1.399 + // 1.400 + 1.401 + char *msg = ::PR_smprintf("SQL statement (%x) should have been finalized" 1.402 + " before garbage-collection. For more details on this statement, set" 1.403 + " NSPR_LOG_MESSAGES=mozStorage:5 .", 1.404 + mDBStatement); 1.405 + 1.406 + // 1.407 + // Note that we can't display the statement itself, as the data structure 1.408 + // is not valid anymore. However, the address shown here should help 1.409 + // developers correlate with the more complete debug message triggered 1.410 + // by AsyncClose(). 1.411 + // 1.412 + 1.413 +#if 0 1.414 + // Deactivate the warning until we have fixed the exising culprit 1.415 + // (see bug 914070). 1.416 + NS_WARNING(msg); 1.417 +#endif // 0 1.418 + 1.419 + PR_LOG(gStorageLog, PR_LOG_WARNING, (msg)); 1.420 + 1.421 + ::PR_smprintf_free(msg); 1.422 + } 1.423 + 1.424 +#endif 1.425 + 1.426 + mDBStatement = nullptr; 1.427 + 1.428 + if (mAsyncStatement) { 1.429 + // If the destructor called us, there are no pending async statements (they 1.430 + // hold a reference to us) and we can/must just kill the statement directly. 1.431 + if (aDestructing) 1.432 + destructorAsyncFinalize(); 1.433 + else 1.434 + asyncFinalize(); 1.435 + } 1.436 + 1.437 + // We are considered dead at this point, so any wrappers for row or params 1.438 + // need to lose their reference to us. 1.439 + if (mStatementParamsHolder) { 1.440 + nsCOMPtr<nsIXPConnectWrappedNative> wrapper = 1.441 + do_QueryInterface(mStatementParamsHolder); 1.442 + nsCOMPtr<mozIStorageStatementParams> iParams = 1.443 + do_QueryWrappedNative(wrapper); 1.444 + StatementParams *params = static_cast<StatementParams *>(iParams.get()); 1.445 + params->mStatement = nullptr; 1.446 + mStatementParamsHolder = nullptr; 1.447 + } 1.448 + 1.449 + if (mStatementRowHolder) { 1.450 + nsCOMPtr<nsIXPConnectWrappedNative> wrapper = 1.451 + do_QueryInterface(mStatementRowHolder); 1.452 + nsCOMPtr<mozIStorageStatementRow> iRow = 1.453 + do_QueryWrappedNative(wrapper); 1.454 + StatementRow *row = static_cast<StatementRow *>(iRow.get()); 1.455 + row->mStatement = nullptr; 1.456 + mStatementRowHolder = nullptr; 1.457 + } 1.458 + 1.459 + return convertResultCode(srv); 1.460 +} 1.461 + 1.462 +NS_IMETHODIMP 1.463 +Statement::GetParameterCount(uint32_t *_parameterCount) 1.464 +{ 1.465 + if (!mDBStatement) 1.466 + return NS_ERROR_NOT_INITIALIZED; 1.467 + 1.468 + *_parameterCount = mParamCount; 1.469 + return NS_OK; 1.470 +} 1.471 + 1.472 +NS_IMETHODIMP 1.473 +Statement::GetParameterName(uint32_t aParamIndex, 1.474 + nsACString &_name) 1.475 +{ 1.476 + if (!mDBStatement) 1.477 + return NS_ERROR_NOT_INITIALIZED; 1.478 + ENSURE_INDEX_VALUE(aParamIndex, mParamCount); 1.479 + 1.480 + const char *name = ::sqlite3_bind_parameter_name(mDBStatement, 1.481 + aParamIndex + 1); 1.482 + if (name == nullptr) { 1.483 + // this thing had no name, so fake one 1.484 + nsAutoCString name(":"); 1.485 + name.AppendInt(aParamIndex); 1.486 + _name.Assign(name); 1.487 + } 1.488 + else { 1.489 + _name.Assign(nsDependentCString(name)); 1.490 + } 1.491 + 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +NS_IMETHODIMP 1.496 +Statement::GetParameterIndex(const nsACString &aName, 1.497 + uint32_t *_index) 1.498 +{ 1.499 + if (!mDBStatement) 1.500 + return NS_ERROR_NOT_INITIALIZED; 1.501 + 1.502 + // We do not accept any forms of names other than ":name", but we need to add 1.503 + // the colon for SQLite. 1.504 + nsAutoCString name(":"); 1.505 + name.Append(aName); 1.506 + int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get()); 1.507 + if (ind == 0) // Named parameter not found. 1.508 + return NS_ERROR_INVALID_ARG; 1.509 + 1.510 + *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based. 1.511 + 1.512 + return NS_OK; 1.513 +} 1.514 + 1.515 +NS_IMETHODIMP 1.516 +Statement::GetColumnCount(uint32_t *_columnCount) 1.517 +{ 1.518 + if (!mDBStatement) 1.519 + return NS_ERROR_NOT_INITIALIZED; 1.520 + 1.521 + *_columnCount = mResultColumnCount; 1.522 + return NS_OK; 1.523 +} 1.524 + 1.525 +NS_IMETHODIMP 1.526 +Statement::GetColumnName(uint32_t aColumnIndex, 1.527 + nsACString &_name) 1.528 +{ 1.529 + if (!mDBStatement) 1.530 + return NS_ERROR_NOT_INITIALIZED; 1.531 + ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount); 1.532 + 1.533 + const char *cname = ::sqlite3_column_name(mDBStatement, aColumnIndex); 1.534 + _name.Assign(nsDependentCString(cname)); 1.535 + 1.536 + return NS_OK; 1.537 +} 1.538 + 1.539 +NS_IMETHODIMP 1.540 +Statement::GetColumnIndex(const nsACString &aName, 1.541 + uint32_t *_index) 1.542 +{ 1.543 + if (!mDBStatement) 1.544 + return NS_ERROR_NOT_INITIALIZED; 1.545 + 1.546 + // Surprisingly enough, SQLite doesn't provide an API for this. We have to 1.547 + // determine it ourselves sadly. 1.548 + for (uint32_t i = 0; i < mResultColumnCount; i++) { 1.549 + if (mColumnNames[i].Equals(aName)) { 1.550 + *_index = i; 1.551 + return NS_OK; 1.552 + } 1.553 + } 1.554 + 1.555 + return NS_ERROR_INVALID_ARG; 1.556 +} 1.557 + 1.558 +NS_IMETHODIMP 1.559 +Statement::Reset() 1.560 +{ 1.561 + if (!mDBStatement) 1.562 + return NS_ERROR_NOT_INITIALIZED; 1.563 + 1.564 +#ifdef DEBUG 1.565 + PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Resetting statement: '%s'", 1.566 + ::sqlite3_sql(mDBStatement))); 1.567 + 1.568 + checkAndLogStatementPerformance(mDBStatement); 1.569 +#endif 1.570 + 1.571 + mParamsArray = nullptr; 1.572 + (void)sqlite3_reset(mDBStatement); 1.573 + (void)sqlite3_clear_bindings(mDBStatement); 1.574 + 1.575 + mExecuting = false; 1.576 + 1.577 + return NS_OK; 1.578 +} 1.579 + 1.580 +NS_IMETHODIMP 1.581 +Statement::BindParameters(mozIStorageBindingParamsArray *aParameters) 1.582 +{ 1.583 + if (!mDBStatement) 1.584 + return NS_ERROR_NOT_INITIALIZED; 1.585 + 1.586 + BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters); 1.587 + if (array->getOwner() != this) 1.588 + return NS_ERROR_UNEXPECTED; 1.589 + 1.590 + if (array->length() == 0) 1.591 + return NS_ERROR_UNEXPECTED; 1.592 + 1.593 + mParamsArray = array; 1.594 + mParamsArray->lock(); 1.595 + 1.596 + return NS_OK; 1.597 +} 1.598 + 1.599 +NS_IMETHODIMP 1.600 +Statement::Execute() 1.601 +{ 1.602 + if (!mDBStatement) 1.603 + return NS_ERROR_NOT_INITIALIZED; 1.604 + 1.605 + bool ret; 1.606 + nsresult rv = ExecuteStep(&ret); 1.607 + nsresult rv2 = Reset(); 1.608 + 1.609 + return NS_FAILED(rv) ? rv : rv2; 1.610 +} 1.611 + 1.612 +NS_IMETHODIMP 1.613 +Statement::ExecuteStep(bool *_moreResults) 1.614 +{ 1.615 + PROFILER_LABEL("storage", "Statement::ExecuteStep"); 1.616 + if (!mDBStatement) 1.617 + return NS_ERROR_NOT_INITIALIZED; 1.618 + 1.619 + // Bind any parameters first before executing. 1.620 + if (mParamsArray) { 1.621 + // If we have more than one row of parameters to bind, they shouldn't be 1.622 + // calling this method (and instead use executeAsync). 1.623 + if (mParamsArray->length() != 1) 1.624 + return NS_ERROR_UNEXPECTED; 1.625 + 1.626 + BindingParamsArray::iterator row = mParamsArray->begin(); 1.627 + nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = 1.628 + do_QueryInterface(*row); 1.629 + nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement); 1.630 + if (error) { 1.631 + int32_t srv; 1.632 + (void)error->GetResult(&srv); 1.633 + return convertResultCode(srv); 1.634 + } 1.635 + 1.636 + // We have bound, so now we can clear our array. 1.637 + mParamsArray = nullptr; 1.638 + } 1.639 + int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement); 1.640 + 1.641 +#ifdef PR_LOGGING 1.642 + if (srv != SQLITE_ROW && srv != SQLITE_DONE) { 1.643 + nsAutoCString errStr; 1.644 + (void)mDBConnection->GetLastErrorString(errStr); 1.645 + PR_LOG(gStorageLog, PR_LOG_DEBUG, 1.646 + ("Statement::ExecuteStep error: %s", errStr.get())); 1.647 + } 1.648 +#endif 1.649 + 1.650 + // SQLITE_ROW and SQLITE_DONE are non-errors 1.651 + if (srv == SQLITE_ROW) { 1.652 + // we got a row back 1.653 + mExecuting = true; 1.654 + *_moreResults = true; 1.655 + return NS_OK; 1.656 + } 1.657 + else if (srv == SQLITE_DONE) { 1.658 + // statement is done (no row returned) 1.659 + mExecuting = false; 1.660 + *_moreResults = false; 1.661 + return NS_OK; 1.662 + } 1.663 + else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) { 1.664 + mExecuting = false; 1.665 + } 1.666 + else if (mExecuting) { 1.667 + PR_LOG(gStorageLog, PR_LOG_ERROR, 1.668 + ("SQLite error after mExecuting was true!")); 1.669 + mExecuting = false; 1.670 + } 1.671 + 1.672 + return convertResultCode(srv); 1.673 +} 1.674 + 1.675 +NS_IMETHODIMP 1.676 +Statement::GetState(int32_t *_state) 1.677 +{ 1.678 + if (!mDBStatement) 1.679 + *_state = MOZ_STORAGE_STATEMENT_INVALID; 1.680 + else if (mExecuting) 1.681 + *_state = MOZ_STORAGE_STATEMENT_EXECUTING; 1.682 + else 1.683 + *_state = MOZ_STORAGE_STATEMENT_READY; 1.684 + 1.685 + return NS_OK; 1.686 +} 1.687 + 1.688 +NS_IMETHODIMP 1.689 +Statement::GetColumnDecltype(uint32_t aParamIndex, 1.690 + nsACString &_declType) 1.691 +{ 1.692 + if (!mDBStatement) 1.693 + return NS_ERROR_NOT_INITIALIZED; 1.694 + 1.695 + ENSURE_INDEX_VALUE(aParamIndex, mResultColumnCount); 1.696 + 1.697 + _declType.Assign(::sqlite3_column_decltype(mDBStatement, aParamIndex)); 1.698 + return NS_OK; 1.699 +} 1.700 + 1.701 +//////////////////////////////////////////////////////////////////////////////// 1.702 +//// mozIStorageValueArray (now part of mozIStorageStatement too) 1.703 + 1.704 +NS_IMETHODIMP 1.705 +Statement::GetNumEntries(uint32_t *_length) 1.706 +{ 1.707 + *_length = mResultColumnCount; 1.708 + return NS_OK; 1.709 +} 1.710 + 1.711 +NS_IMETHODIMP 1.712 +Statement::GetTypeOfIndex(uint32_t aIndex, 1.713 + int32_t *_type) 1.714 +{ 1.715 + if (!mDBStatement) 1.716 + return NS_ERROR_NOT_INITIALIZED; 1.717 + 1.718 + ENSURE_INDEX_VALUE(aIndex, mResultColumnCount); 1.719 + 1.720 + if (!mExecuting) 1.721 + return NS_ERROR_UNEXPECTED; 1.722 + 1.723 + int t = ::sqlite3_column_type(mDBStatement, aIndex); 1.724 + switch (t) { 1.725 + case SQLITE_INTEGER: 1.726 + *_type = mozIStorageStatement::VALUE_TYPE_INTEGER; 1.727 + break; 1.728 + case SQLITE_FLOAT: 1.729 + *_type = mozIStorageStatement::VALUE_TYPE_FLOAT; 1.730 + break; 1.731 + case SQLITE_TEXT: 1.732 + *_type = mozIStorageStatement::VALUE_TYPE_TEXT; 1.733 + break; 1.734 + case SQLITE_BLOB: 1.735 + *_type = mozIStorageStatement::VALUE_TYPE_BLOB; 1.736 + break; 1.737 + case SQLITE_NULL: 1.738 + *_type = mozIStorageStatement::VALUE_TYPE_NULL; 1.739 + break; 1.740 + default: 1.741 + return NS_ERROR_FAILURE; 1.742 + } 1.743 + 1.744 + return NS_OK; 1.745 +} 1.746 + 1.747 +NS_IMETHODIMP 1.748 +Statement::GetInt32(uint32_t aIndex, 1.749 + int32_t *_value) 1.750 +{ 1.751 + if (!mDBStatement) 1.752 + return NS_ERROR_NOT_INITIALIZED; 1.753 + 1.754 + ENSURE_INDEX_VALUE(aIndex, mResultColumnCount); 1.755 + 1.756 + if (!mExecuting) 1.757 + return NS_ERROR_UNEXPECTED; 1.758 + 1.759 + *_value = ::sqlite3_column_int(mDBStatement, aIndex); 1.760 + return NS_OK; 1.761 +} 1.762 + 1.763 +NS_IMETHODIMP 1.764 +Statement::GetInt64(uint32_t aIndex, 1.765 + int64_t *_value) 1.766 +{ 1.767 + if (!mDBStatement) 1.768 + return NS_ERROR_NOT_INITIALIZED; 1.769 + 1.770 + ENSURE_INDEX_VALUE(aIndex, mResultColumnCount); 1.771 + 1.772 + if (!mExecuting) 1.773 + return NS_ERROR_UNEXPECTED; 1.774 + 1.775 + *_value = ::sqlite3_column_int64(mDBStatement, aIndex); 1.776 + 1.777 + return NS_OK; 1.778 +} 1.779 + 1.780 +NS_IMETHODIMP 1.781 +Statement::GetDouble(uint32_t aIndex, 1.782 + double *_value) 1.783 +{ 1.784 + if (!mDBStatement) 1.785 + return NS_ERROR_NOT_INITIALIZED; 1.786 + 1.787 + ENSURE_INDEX_VALUE(aIndex, mResultColumnCount); 1.788 + 1.789 + if (!mExecuting) 1.790 + return NS_ERROR_UNEXPECTED; 1.791 + 1.792 + *_value = ::sqlite3_column_double(mDBStatement, aIndex); 1.793 + 1.794 + return NS_OK; 1.795 +} 1.796 + 1.797 +NS_IMETHODIMP 1.798 +Statement::GetUTF8String(uint32_t aIndex, 1.799 + nsACString &_value) 1.800 +{ 1.801 + // Get type of Index will check aIndex for us, so we don't have to. 1.802 + int32_t type; 1.803 + nsresult rv = GetTypeOfIndex(aIndex, &type); 1.804 + NS_ENSURE_SUCCESS(rv, rv); 1.805 + if (type == mozIStorageStatement::VALUE_TYPE_NULL) { 1.806 + // NULL columns should have IsVoid set to distinguish them from the empty 1.807 + // string. 1.808 + _value.Truncate(0); 1.809 + _value.SetIsVoid(true); 1.810 + } 1.811 + else { 1.812 + const char *value = 1.813 + reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement, 1.814 + aIndex)); 1.815 + _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex)); 1.816 + } 1.817 + return NS_OK; 1.818 +} 1.819 + 1.820 +NS_IMETHODIMP 1.821 +Statement::GetString(uint32_t aIndex, 1.822 + nsAString &_value) 1.823 +{ 1.824 + // Get type of Index will check aIndex for us, so we don't have to. 1.825 + int32_t type; 1.826 + nsresult rv = GetTypeOfIndex(aIndex, &type); 1.827 + NS_ENSURE_SUCCESS(rv, rv); 1.828 + if (type == mozIStorageStatement::VALUE_TYPE_NULL) { 1.829 + // NULL columns should have IsVoid set to distinguish them from the empty 1.830 + // string. 1.831 + _value.Truncate(0); 1.832 + _value.SetIsVoid(true); 1.833 + } else { 1.834 + const char16_t *value = 1.835 + static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement, 1.836 + aIndex)); 1.837 + _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / 2); 1.838 + } 1.839 + return NS_OK; 1.840 +} 1.841 + 1.842 +NS_IMETHODIMP 1.843 +Statement::GetBlob(uint32_t aIndex, 1.844 + uint32_t *_size, 1.845 + uint8_t **_blob) 1.846 +{ 1.847 + if (!mDBStatement) 1.848 + return NS_ERROR_NOT_INITIALIZED; 1.849 + 1.850 + ENSURE_INDEX_VALUE(aIndex, mResultColumnCount); 1.851 + 1.852 + if (!mExecuting) 1.853 + return NS_ERROR_UNEXPECTED; 1.854 + 1.855 + int size = ::sqlite3_column_bytes(mDBStatement, aIndex); 1.856 + void *blob = nullptr; 1.857 + if (size) { 1.858 + blob = nsMemory::Clone(::sqlite3_column_blob(mDBStatement, aIndex), size); 1.859 + NS_ENSURE_TRUE(blob, NS_ERROR_OUT_OF_MEMORY); 1.860 + } 1.861 + 1.862 + *_blob = static_cast<uint8_t *>(blob); 1.863 + *_size = size; 1.864 + return NS_OK; 1.865 +} 1.866 + 1.867 +NS_IMETHODIMP 1.868 +Statement::GetSharedUTF8String(uint32_t aIndex, 1.869 + uint32_t *_length, 1.870 + const char **_value) 1.871 +{ 1.872 + if (_length) 1.873 + *_length = ::sqlite3_column_bytes(mDBStatement, aIndex); 1.874 + 1.875 + *_value = reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement, 1.876 + aIndex)); 1.877 + return NS_OK; 1.878 +} 1.879 + 1.880 +NS_IMETHODIMP 1.881 +Statement::GetSharedString(uint32_t aIndex, 1.882 + uint32_t *_length, 1.883 + const char16_t **_value) 1.884 +{ 1.885 + if (_length) 1.886 + *_length = ::sqlite3_column_bytes16(mDBStatement, aIndex); 1.887 + 1.888 + *_value = static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement, 1.889 + aIndex)); 1.890 + return NS_OK; 1.891 +} 1.892 + 1.893 +NS_IMETHODIMP 1.894 +Statement::GetSharedBlob(uint32_t aIndex, 1.895 + uint32_t *_size, 1.896 + const uint8_t **_blob) 1.897 +{ 1.898 + *_size = ::sqlite3_column_bytes(mDBStatement, aIndex); 1.899 + *_blob = static_cast<const uint8_t *>(::sqlite3_column_blob(mDBStatement, 1.900 + aIndex)); 1.901 + return NS_OK; 1.902 +} 1.903 + 1.904 +NS_IMETHODIMP 1.905 +Statement::GetIsNull(uint32_t aIndex, 1.906 + bool *_isNull) 1.907 +{ 1.908 + // Get type of Index will check aIndex for us, so we don't have to. 1.909 + int32_t type; 1.910 + nsresult rv = GetTypeOfIndex(aIndex, &type); 1.911 + NS_ENSURE_SUCCESS(rv, rv); 1.912 + *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL); 1.913 + return NS_OK; 1.914 +} 1.915 + 1.916 +//////////////////////////////////////////////////////////////////////////////// 1.917 +//// mozIStorageBindingParams 1.918 + 1.919 +BOILERPLATE_BIND_PROXIES( 1.920 + Statement, 1.921 + if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED; 1.922 +) 1.923 + 1.924 +} // namespace storage 1.925 +} // namespace mozilla