| /* |
| * Copyright (C) 2006-2021 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "SQLiteStatement.h" |
| |
| #include "Logging.h" |
| #include "SQLValue.h" |
| #include "SQLiteDatabaseTracker.h" |
| #include <sqlite3.h> |
| #include <variant> |
| #include <wtf/Assertions.h> |
| #include <wtf/text/StringView.h> |
| |
| // SQLite 3.6.16 makes sqlite3_prepare_v2 automatically retry preparing the statement |
| // once if the database scheme has changed. We rely on this behavior. |
| #if SQLITE_VERSION_NUMBER < 3006016 |
| #error SQLite version 3.6.16 or newer is required |
| #endif |
| |
| namespace WebCore { |
| |
| SQLiteStatement::SQLiteStatement(SQLiteDatabase& db, sqlite3_stmt* statement) |
| : m_database(db) |
| , m_statement(statement) |
| { |
| ASSERT(statement); |
| m_database.incrementStatementCount(); |
| } |
| |
| SQLiteStatement::SQLiteStatement(SQLiteStatement&& other) |
| : m_database(other.database()) |
| , m_statement(std::exchange(other.m_statement, nullptr)) |
| { |
| m_database.incrementStatementCount(); |
| } |
| |
| SQLiteStatement::~SQLiteStatement() |
| { |
| sqlite3_finalize(m_statement); |
| m_database.decrementStatementCount(); |
| } |
| |
| int SQLiteStatement::step() |
| { |
| Locker databaseLock { m_database.databaseMutex() }; |
| |
| // If we're not within a transaction and we call sqlite3_step(), SQLite will implicitly create a transaction for us. |
| // In such case, we should bump our transaction count to reflect that. |
| std::optional<SQLiteTransactionInProgressAutoCounter> transactionCounter; |
| if (!m_database.transactionInProgress() && !isReadOnly()) |
| transactionCounter.emplace(); |
| |
| int error = sqlite3_step(m_statement); |
| if (error != SQLITE_DONE && error != SQLITE_ROW) |
| LOG(SQLDatabase, "sqlite3_step failed (%i)\nError - %s", error, sqlite3_errmsg(m_database.sqlite3Handle())); |
| |
| return error; |
| } |
| |
| int SQLiteStatement::reset() |
| { |
| int status = sqlite3_reset(m_statement); |
| sqlite3_clear_bindings(m_statement); |
| return status; |
| } |
| |
| bool SQLiteStatement::executeCommand() |
| { |
| return step() == SQLITE_DONE; |
| } |
| |
| int SQLiteStatement::bindBlob(int index, Span<const uint8_t> blob) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| ASSERT(blob.data() || !blob.size()); |
| |
| return sqlite3_bind_blob(m_statement, index, blob.data(), blob.size(), SQLITE_TRANSIENT); |
| } |
| |
| int SQLiteStatement::bindBlob(int index, const String& text) |
| { |
| // String::characters() returns 0 for the empty string, which SQLite |
| // treats as a null, so we supply a non-null pointer for that case. |
| auto upconvertedCharacters = StringView(text).upconvertedCharacters(); |
| UChar anyCharacter = 0; |
| const UChar* characters; |
| if (text.isEmpty() && !text.isNull()) |
| characters = &anyCharacter; |
| else |
| characters = upconvertedCharacters; |
| |
| return bindBlob(index, Span { reinterpret_cast<const uint8_t*>(characters), text.length() * sizeof(UChar) }); |
| } |
| |
| int SQLiteStatement::bindText(int index, StringView text) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| |
| // Fast path when the input text is all ASCII. |
| if (text.is8Bit() && text.isAllASCII()) |
| return sqlite3_bind_text(m_statement, index, text.length() ? reinterpret_cast<const char*>(text.characters8()) : "", text.length(), SQLITE_TRANSIENT); |
| |
| auto utf8Text = text.utf8(); |
| return sqlite3_bind_text(m_statement, index, utf8Text.data(), utf8Text.length(), SQLITE_TRANSIENT); |
| } |
| |
| int SQLiteStatement::bindInt(int index, int integer) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| |
| return sqlite3_bind_int(m_statement, index, integer); |
| } |
| |
| int SQLiteStatement::bindInt64(int index, int64_t integer) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| |
| return sqlite3_bind_int64(m_statement, index, integer); |
| } |
| |
| int SQLiteStatement::bindDouble(int index, double number) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| |
| return sqlite3_bind_double(m_statement, index, number); |
| } |
| |
| int SQLiteStatement::bindNull(int index) |
| { |
| ASSERT(index > 0); |
| ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); |
| |
| return sqlite3_bind_null(m_statement, index); |
| } |
| |
| int SQLiteStatement::bindValue(int index, const SQLValue& value) |
| { |
| return WTF::switchOn(value, |
| [&] (const std::nullptr_t&) { return bindNull(index); }, |
| [&] (const String& string) { return bindText(index, string); }, |
| [&] (double number) { return bindDouble(index, number); } |
| ); |
| } |
| |
| unsigned SQLiteStatement::bindParameterCount() const |
| { |
| return sqlite3_bind_parameter_count(m_statement); |
| } |
| |
| int SQLiteStatement::columnCount() |
| { |
| return sqlite3_data_count(m_statement); |
| } |
| |
| bool SQLiteStatement::isColumnDeclaredAsBlob(int col) |
| { |
| ASSERT(col >= 0); |
| return equalLettersIgnoringASCIICase(StringView::fromLatin1(sqlite3_column_decltype(m_statement, col)), "blob"_s); |
| } |
| |
| String SQLiteStatement::columnName(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return String(); |
| if (columnCount() <= col) |
| return String(); |
| return String::fromUTF8(sqlite3_column_name(m_statement, col)); |
| } |
| |
| SQLValue SQLiteStatement::columnValue(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return nullptr; |
| if (columnCount() <= col) |
| return nullptr; |
| |
| // SQLite is typed per value. optional column types are |
| // "(mostly) ignored" |
| sqlite3_value* value = sqlite3_column_value(m_statement, col); |
| switch (sqlite3_value_type(value)) { |
| case SQLITE_INTEGER: // SQLValue and JS don't represent integers, so use FLOAT -case |
| case SQLITE_FLOAT: |
| return sqlite3_value_double(value); |
| case SQLITE_BLOB: // SQLValue and JS don't represent blobs, so use TEXT -case |
| case SQLITE_TEXT: |
| return String::fromUTF8(sqlite3_value_text(value), sqlite3_value_bytes(value)); |
| case SQLITE_NULL: |
| return nullptr; |
| default: |
| break; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return nullptr; |
| } |
| |
| String SQLiteStatement::columnText(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return String(); |
| if (columnCount() <= col) |
| return String(); |
| return String::fromUTF8(sqlite3_column_text(m_statement, col), sqlite3_column_bytes(m_statement, col)); |
| } |
| |
| double SQLiteStatement::columnDouble(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return 0.0; |
| if (columnCount() <= col) |
| return 0.0; |
| return sqlite3_column_double(m_statement, col); |
| } |
| |
| int SQLiteStatement::columnInt(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return 0; |
| if (columnCount() <= col) |
| return 0; |
| return sqlite3_column_int(m_statement, col); |
| } |
| |
| int64_t SQLiteStatement::columnInt64(int col) |
| { |
| ASSERT(col >= 0); |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return 0; |
| if (columnCount() <= col) |
| return 0; |
| return sqlite3_column_int64(m_statement, col); |
| } |
| |
| String SQLiteStatement::columnBlobAsString(int col) |
| { |
| ASSERT(col >= 0); |
| |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return String(); |
| |
| if (columnCount() <= col) |
| return String(); |
| |
| const void* blob = sqlite3_column_blob(m_statement, col); |
| if (!blob) |
| return emptyString(); |
| |
| int size = sqlite3_column_bytes(m_statement, col); |
| if (size < 0) |
| return String(); |
| |
| ASSERT(!(size % sizeof(UChar))); |
| return StringImpl::create8BitIfPossible(static_cast<const UChar*>(blob), size / sizeof(UChar)); |
| } |
| |
| Vector<uint8_t> SQLiteStatement::columnBlob(int col) |
| { |
| auto span = columnBlobAsSpan(col); |
| return { span.data(), span.size() }; |
| } |
| |
| Span<const uint8_t> SQLiteStatement::columnBlobAsSpan(int col) |
| { |
| ASSERT(col >= 0); |
| |
| if (!hasStartedStepping() && step() != SQLITE_ROW) |
| return { }; |
| |
| if (columnCount() <= col) |
| return { }; |
| |
| const void* blob = sqlite3_column_blob(m_statement, col); |
| if (!blob) |
| return { }; |
| |
| int blobSize = sqlite3_column_bytes(m_statement, col); |
| if (blobSize <= 0) |
| return { }; |
| |
| return { static_cast<const uint8_t*>(blob), static_cast<size_t>(blobSize) }; |
| } |
| |
| bool SQLiteStatement::hasStartedStepping() |
| { |
| return sqlite3_stmt_busy(m_statement); |
| } |
| |
| bool SQLiteStatement::isReadOnly() |
| { |
| return sqlite3_stmt_readonly(m_statement); |
| } |
| |
| } // namespace WebCore |