storage/src/mozStorageStatement.cpp

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

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

Ignore runtime configuration files generated during quality assurance.

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

mercurial