/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "sqlitebasestatement.h" #include "sqlitedatabase.h" #include "sqlitedatabasebackend.h" #include "sqliteexception.h" #include "sqlite3.h" #include #include #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif namespace Sqlite { BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database) : m_compiledStatement(nullptr, deleteCompiledStatement) , m_database(database) , m_bindingParameterCount(0) , m_columnCount(0) { prepare(sqlStatement); setBindingParameterCount(); setColumnCount(); } void BaseStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement) { sqlite3_finalize(compiledStatement); } class UnlockNotification { public: static void unlockNotifyCallBack(void **arguments, int argumentCount) { for (int index = 0; index < argumentCount; index++) { UnlockNotification *unlockNotification = static_cast(arguments[index]); unlockNotification->wakeupWaitCondition(); } } void wakeupWaitCondition() { { std::lock_guard lock(m_mutex); m_fired = 1; } m_waitCondition.notify_all(); } void wait() { std::unique_lock lock(m_mutex); m_waitCondition.wait(lock, [&] () { return m_fired; }); } private: bool m_fired = false; std::condition_variable m_waitCondition; std::mutex m_mutex; }; void BaseStatement::waitForUnlockNotify() const { UnlockNotification unlockNotification; int resultCode = sqlite3_unlock_notify(sqliteDatabaseHandle(), UnlockNotification::unlockNotifyCallBack, &unlockNotification); if (resultCode == SQLITE_LOCKED) throw DeadLock("SqliteStatement::waitForUnlockNotify: database is in a dead lock!"); unlockNotification.wait(); } void BaseStatement::reset() const { int resultCode = sqlite3_reset(m_compiledStatement.get()); if (resultCode != SQLITE_OK) checkForResetError(resultCode); } bool BaseStatement::next() const { int resultCode; do { resultCode = sqlite3_step(m_compiledStatement.get()); if (resultCode == SQLITE_LOCKED) { waitForUnlockNotify(); sqlite3_reset(m_compiledStatement.get()); } } while (resultCode == SQLITE_LOCKED); if (resultCode == SQLITE_ROW) return true; else if (resultCode == SQLITE_DONE) return false; checkForStepError(resultCode); } void BaseStatement::step() const { next(); } int BaseStatement::columnCount() const { return m_columnCount; } void BaseStatement::bind(int index, NullValue) { int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, int value) { int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, long long value) { int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, double value) { int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, void *pointer) { int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, Utils::SmallStringView text) { int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, text.data(), int(text.size()), SQLITE_STATIC); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, Utils::span bytes) { int resultCode = sqlite3_bind_blob64(m_compiledStatement.get(), index, bytes.data(), static_cast(bytes.size()), SQLITE_STATIC); if (resultCode != SQLITE_OK) checkForBindingError(resultCode); } void BaseStatement::bind(int index, const Value &value) { switch (value.type()) { case ValueType::Integer: bind(index, value.toInteger()); break; case ValueType::Float: bind(index, value.toFloat()); break; case ValueType::String: bind(index, value.toStringView()); break; } } void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { int resultCode; do { sqlite3_stmt *sqliteStatement = nullptr; resultCode = sqlite3_prepare_v2(sqliteDatabaseHandle(), sqlStatement.data(), int(sqlStatement.size()), &sqliteStatement, nullptr); m_compiledStatement.reset(sqliteStatement); if (resultCode == SQLITE_LOCKED) waitForUnlockNotify(); } while (resultCode == SQLITE_LOCKED); if (resultCode != SQLITE_OK) checkForPrepareError(resultCode); } sqlite3 *BaseStatement::sqliteDatabaseHandle() const { return m_database.backend().sqliteDatabaseHandle(); } void BaseStatement::checkForStepError(int resultCode) const { switch (resultCode) { case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); } throwUnknowError("SqliteStatement::stepStatement: unknown error has happened"); } void BaseStatement::checkForResetError(int resultCode) const { switch (resultCode) { case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); } throwUnknowError("SqliteStatement::reset: unknown error has happened"); } void BaseStatement::checkForPrepareError(int resultCode) const { switch (resultCode) { case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!"); case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!"); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!"); case SQLITE_IOERR: throwIoError("SqliteStatement::prepareStatement: IO error happened!"); } throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened"); } void BaseStatement::checkForBindingError(int resultCode) const { switch (resultCode) { case SQLITE_TOOBIG: throwBingingTooBig("SqliteStatement::bind: string or blob are over size limits(SQLITE_LIMIT_LENGTH)!"); case SQLITE_RANGE : throwBindingIndexIsOutOfRange("SqliteStatement::bind: binding index is out of range!"); case SQLITE_NOMEM: throw std::bad_alloc(); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::bind: was called inappropriately!"); } throwUnknowError("SqliteStatement::bind: unknown error has happened"); } void BaseStatement::checkColumnCount(int columnCount) const { if (columnCount != m_columnCount) throw ColumnCountDoesNotMatch("SqliteStatement::values: column count does not match!"); } void BaseStatement::checkBindingName(int index) const { if (index <= 0 || index > m_bindingParameterCount) throwWrongBingingName("SqliteStatement::bind: binding name are not exists in this statement!"); } void BaseStatement::setBindingParameterCount() { m_bindingParameterCount = sqlite3_bind_parameter_count(m_compiledStatement.get()); } Utils::SmallStringView chopFirstLetter(const char *rawBindingName) { if (rawBindingName != nullptr) return Utils::SmallStringView(++rawBindingName); return Utils::SmallStringView(""); } void BaseStatement::setColumnCount() { m_columnCount = sqlite3_column_count(m_compiledStatement.get()); } bool BaseStatement::isReadOnlyStatement() const { return sqlite3_stmt_readonly(m_compiledStatement.get()); } void BaseStatement::throwStatementIsBusy(const char *whatHasHappened) const { throw StatementIsBusy(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } void BaseStatement::throwStatementHasError(const char *whatHasHappened) const { throw StatementHasError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } void BaseStatement::throwStatementIsMisused(const char *whatHasHappened) const { throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } void BaseStatement::throwIoError(const char *whatHasHappened) const { throw IoError(whatHasHappened); } void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const { throw ConstraintPreventsModification(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } void BaseStatement::throwNoValuesToFetch(const char *whatHasHappened) const { throw NoValuesToFetch(whatHasHappened); } void BaseStatement::throwBindingIndexIsOutOfRange(const char *whatHasHappened) const { throw BindingIndexIsOutOfRange(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } void BaseStatement::throwWrongBingingName(const char *whatHasHappened) const { throw WrongBindingName(whatHasHappened); } void BaseStatement::throwUnknowError(const char *whatHasHappened) const { if (sqliteDatabaseHandle()) throw UnknowError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); else throw UnknowError(whatHasHappened); } void BaseStatement::throwBingingTooBig(const char *whatHasHappened) const { throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } QString BaseStatement::columnName(int column) const { return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column)); } Database &BaseStatement::database() const { return m_database; } namespace { template StringType textForColumn(sqlite3_stmt *sqlStatment, int column) { const char *text = reinterpret_cast(sqlite3_column_text(sqlStatment, column)); std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column)); return StringType(text, size); } Utils::span blobForColumn(sqlite3_stmt *sqlStatment, int column) { const byte *blob = reinterpret_cast(sqlite3_column_blob(sqlStatment, column)); std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column)); return {blob, size}; } Utils::span convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column) { int dataType = sqlite3_column_type(sqlStatment, column); if (dataType == SQLITE_BLOB) return blobForColumn(sqlStatment, column); return {}; } template StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) { int dataType = sqlite3_column_type(sqlStatment, column); switch (dataType) { case SQLITE_INTEGER: case SQLITE_FLOAT: case SQLITE3_TEXT: return textForColumn(sqlStatment, column); case SQLITE_BLOB: case SQLITE_NULL: break; } return StringType{"", 0}; } } // namespace int BaseStatement::fetchIntValue(int column) const { return sqlite3_column_int(m_compiledStatement.get(), column); } template<> int BaseStatement::fetchValue(int column) const { return fetchIntValue(column); } long BaseStatement::fetchLongValue(int column) const { return long(fetchValue(column)); } template<> long BaseStatement::fetchValue(int column) const { return fetchLongValue(column); } long long BaseStatement::fetchLongLongValue(int column) const { return sqlite3_column_int64(m_compiledStatement.get(), column); } template<> long long BaseStatement::fetchValue(int column) const { return fetchLongLongValue(column); } double BaseStatement::fetchDoubleValue(int column) const { return sqlite3_column_double(m_compiledStatement.get(), column); } Utils::span BaseStatement::fetchBlobValue(int column) const { return convertToBlobForColumn(m_compiledStatement.get(), column); } template<> double BaseStatement::fetchValue(int column) const { return fetchDoubleValue(column); } template StringType BaseStatement::fetchValue(int column) const { return convertToTextForColumn(m_compiledStatement.get(), column); } template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue( int column) const; template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue( int column) const; template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue( int column) const; Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const { return fetchValue(column); } ValueView BaseStatement::fetchValueView(int column) const { int dataType = sqlite3_column_type(m_compiledStatement.get(), column); switch (dataType) { case SQLITE_NULL: return ValueView::create(NullValue{}); case SQLITE_INTEGER: return ValueView::create(fetchLongLongValue(column)); case SQLITE_FLOAT: return ValueView::create(fetchDoubleValue(column)); case SQLITE3_TEXT: return ValueView::create(fetchValue(column)); case SQLITE_BLOB: break; } return ValueView::create(NullValue{}); } } // namespace Sqlite