/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
nsCString* columnNames = mColumnNames.AppendElements(mResultColumnCount); for (uint32_t i = 0; i < mResultColumnCount; i++) { constchar* name = ::sqlite3_column_name(mDBStatement, i);
columnNames[i].Assign(name);
}
#ifdef DEBUG // We want to try and test for LIKE and that consumers are using // escapeStringForLIKE instead of just trusting user input. The idea to // check to see if they are binding a parameter after like instead of just // using a string. We only do this in debug builds because it's expensive! auto c = nsCaseInsensitiveCStringComparator;
nsACString::const_iterator start, end, e;
aSQLStatement.BeginReading(start);
aSQLStatement.EndReading(end);
e = end; while (::FindInReadable(" LIKE"_ns, start, e, c)) { // We have a LIKE in here, so we perform our tests // FindInReadable moves the iterator, so we have to get a new one for // each test we perform.
nsACString::const_iterator s1, s2, s3;
s1 = s2 = s3 = start;
if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
::FindInReadable(" LIKE :"_ns, s2, end, c) ||
::FindInReadable(" LIKE @"_ns, s3, end, c))) { // At this point, we didn't find a LIKE statement followed by ?, :, // or @, all of which are valid characters for binding a parameter. // We will warn the consumer that they may not be safely using LIKE.
NS_WARNING( "Unsafe use of LIKE detected! Please ensure that you " "are using mozIStorageStatement::escapeStringForLIKE " "and that you are binding that result to the statement " "to prevent SQL injection attacks.");
}
// resetting start and e
start = e;
e = end;
} #endif
// If we do not have an array object yet, make it. if (!mParamsArray) {
nsCOMPtr<mozIStorageBindingParamsArray> array;
rv = NewBindingParamsArray(getter_AddRefs(array));
NS_ENSURE_SUCCESS(rv, nullptr);
// If there isn't already any rows added, we'll have to add one to use. if (mParamsArray->length() == 0) {
RefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
NS_ENSURE_TRUE(params, nullptr);
// We have to unlock our params because AddParams locks them. This is safe // because no reference to the params object was, or ever will be given out.
params->unlock(this);
// We also want to lock our array at this point - we don't want anything to // be added to it. Nothing has, or will ever get a reference to it, but we // will get additional safety checks via assertions by doing this.
mParamsArray->lock();
}
return *mParamsArray->begin();
}
void Statement::MaybeRecordQueryStatus(int srv, bool isResetting) { // If the statement hasn't been executed synchronously since it was last reset // or created then there is no need to record anything. Asynchronous // statements have their status tracked and recorded by StatementData. if (!mHasExecuted) { return;
}
if (!isResetting && !isErrorCode(srv)) { // Non-errors will be recorded when finalizing. return;
}
// We only record a status if no status has been recorded previously. if (!mQueryStatusRecorded && mDBConnection) {
mDBConnection->RecordQueryStatus(srv);
}
// Allow another status to be recorded if we are resetting this statement.
mQueryStatusRecorded = !isResetting;
}
int Statement::getAsyncStatement(sqlite3_stmt** _stmt) { // If we have no statement, we shouldn't be calling this method!
NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
// If we do not yet have a cached async statement, clone our statement now. if (!mAsyncStatement) {
nsDependentCString sql(::sqlite3_sql(mDBStatement)); int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
&mAsyncStatement); if (rc != SQLITE_OK) {
mDBConnection->RecordQueryStatus(rc);
*_stmt = nullptr; return rc;
}
MOZ_LOG(gStorageLog, LogLevel::Debug,
("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
}
*_stmt = mAsyncStatement; return SQLITE_OK;
}
nsresult Statement::getAsynchronousStatementData(StatementData& _data) { if (!mDBStatement) return NS_ERROR_UNEXPECTED;
sqlite3_stmt* stmt; int rc = getAsyncStatement(&stmt); if (rc != SQLITE_OK) return convertResultCode(rc);
nsresult Statement::internalFinalize(bool aDestructing) { if (!mDBStatement) return NS_OK;
int srv = SQLITE_OK;
{ // If the statement ends up being finalized twice, the second finalization // would apply to a dangling pointer and may cause unexpected consequences. // Thus we must be sure that the connection state won't change during this // operation, to avoid racing with finalizations made by the closing // connection. See Connection::internalClose().
MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex); if (!mDBConnection->isClosed(lockedScope)) {
MOZ_LOG(gStorageLog, LogLevel::Debug,
("Finalizing statement '%s' during garbage-collection",
::sqlite3_sql(mDBStatement)));
srv = ::sqlite3_finalize(mDBStatement);
} #ifdef DEBUG else { // The database connection is closed. The sqlite // statement has either been finalized already by the connection // or is about to be finalized by the connection. // // Finalizing it here would be useless and segfaultish. // // Note that we can't display the statement itself, as the data structure // is not valid anymore. However, the address shown here should help // developers correlate with the more complete debug message triggered // by AsyncClose().
SmprintfPointer msg = ::mozilla::Smprintf( "SQL statement (%p) should have been finalized" " before garbage-collection. For more details on this statement, set" " NSPR_LOG_MESSAGES=mozStorage:5 .",
mDBStatement);
NS_WARNING(msg.get());
// Use %s so we aren't exposing random strings to printf interpolation.
MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
} #endif// DEBUG
}
// This will be a no-op if the status has already been recorded or if this // statement has not been executed. Async statements have their status // tracked and recorded in StatementData.
MaybeRecordQueryStatus(srv, true);
mDBStatement = nullptr;
if (mAsyncStatement) { // If the destructor called us, there are no pending async statements (they // hold a reference to us) and we can/must just kill the statement directly. if (aDestructing)
destructorAsyncFinalize(); else
asyncFinalize();
}
// Release the holders, so they can release the reference to us.
mStatementParamsHolder = nullptr;
mStatementRowHolder = nullptr;
return convertResultCode(srv);
}
NS_IMETHODIMP
Statement::GetParameterCount(uint32_t* _parameterCount) { if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
constchar* name =
::sqlite3_bind_parameter_name(mDBStatement, aParamIndex + 1); if (name == nullptr) { // this thing had no name, so fake one
nsAutoCString fakeName(":");
fakeName.AppendInt(aParamIndex);
_name.Assign(fakeName);
} else {
_name.Assign(nsDependentCString(name));
}
// We do not accept any forms of names other than ":name", but we need to add // the colon for SQLite.
nsAutoCString name(":");
name.Append(aName); int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get()); if (ind == 0) // Named parameter not found. return NS_ERROR_INVALID_ARG;
*_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
return NS_OK;
}
NS_IMETHODIMP
Statement::GetColumnCount(uint32_t* _columnCount) { if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
// Surprisingly enough, SQLite doesn't provide an API for this. We have to // determine it ourselves sadly. for (uint32_t i = 0; i < mResultColumnCount; i++) { if (mColumnNames[i].Equals(aName)) {
*_index = i; return NS_OK;
}
}
return NS_ERROR_INVALID_ARG;
}
NS_IMETHODIMP
Statement::Reset() { if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
// This will be a no-op if the status has already been recorded or if this // statement has not been executed. Async statements have their status // tracked and recorded in StatementData.
MaybeRecordQueryStatus(SQLITE_OK, true);
mHasExecuted = false;
if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
// Bind any parameters first before executing. if (mParamsArray) { // If we have more than one row of parameters to bind, they shouldn't be // calling this method (and instead use executeAsync). if (mParamsArray->length() != 1) return NS_ERROR_UNEXPECTED;
// We have bound, so now we can clear our array.
mParamsArray = nullptr;
} int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
mHasExecuted = true;
MaybeRecordQueryStatus(srv);
NS_IMETHODIMP
Statement::GetUTF8String(uint32_t aIndex, nsACString& _value) { // Get type of Index will check aIndex for us, so we don't have to.
int32_t type;
nsresult rv = GetTypeOfIndex(aIndex, &type);
NS_ENSURE_SUCCESS(rv, rv); if (type == mozIStorageStatement::VALUE_TYPE_NULL) { // NULL columns should have IsVoid set to distinguish them from the empty // string.
_value.SetIsVoid(true);
} else { constchar* value = reinterpret_cast<constchar*>(
::sqlite3_column_text(mDBStatement, aIndex));
_value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
} return NS_OK;
}
NS_IMETHODIMP
Statement::GetString(uint32_t aIndex, nsAString& _value) { // Get type of Index will check aIndex for us, so we don't have to.
int32_t type;
nsresult rv = GetTypeOfIndex(aIndex, &type);
NS_ENSURE_SUCCESS(rv, rv); if (type == mozIStorageStatement::VALUE_TYPE_NULL) { // NULL columns should have IsVoid set to distinguish them from the empty // string.
_value.SetIsVoid(true);
} else { const char16_t* value = static_cast<const char16_t*>(
::sqlite3_column_text16(mDBStatement, aIndex));
_value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / sizeof(char16_t));
} return NS_OK;
}
NS_IMETHODIMP
Statement::GetIsNull(uint32_t aIndex, bool* _isNull) { // Get type of Index will check aIndex for us, so we don't have to.
int32_t type;
nsresult rv = GetTypeOfIndex(aIndex, &type);
NS_ENSURE_SUCCESS(rv, rv);
*_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL); return NS_OK;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.