summaryrefslogtreecommitdiff
path: root/src/libs/sqlite/sqlitebasestatement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/sqlite/sqlitebasestatement.cpp')
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp500
1 files changed, 500 insertions, 0 deletions
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
new file mode 100644
index 0000000000..3f41c04a11
--- /dev/null
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -0,0 +1,500 @@
+/****************************************************************************
+**
+** 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 <condition_variable>
+#include <mutex>
+
+#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),
+ m_isReadyToFetchValues(false)
+{
+ prepare(sqlStatement);
+ setBindingParameterCount();
+ setColumnCount();
+}
+
+void BaseStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement)
+{
+ if (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<UnlockNotification *>(arguments[index]);
+ unlockNotification->wakeupWaitCondition();
+ }
+ }
+
+ void wakeupWaitCondition()
+ {
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_fired = 1;
+ }
+ m_waitCondition.notify_all();
+ }
+
+ void wait()
+ {
+ std::unique_lock<std::mutex> 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());
+ switch (resultCode) {
+ case SQLITE_OK: return;
+ 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!");
+ }
+
+ m_isReadyToFetchValues = false;
+}
+
+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);
+
+ setIfIsReadyToFetchValues(resultCode);
+
+ return checkForStepError(resultCode);
+}
+
+void BaseStatement::step() const
+{
+ next();
+}
+
+void BaseStatement::execute() const
+{
+ next();
+ reset();
+}
+
+int BaseStatement::columnCount() const
+{
+ return m_columnCount;
+}
+
+Utils::SmallStringVector BaseStatement::columnNames() const
+{
+ Utils::SmallStringVector columnNames;
+ int columnCount = BaseStatement::columnCount();
+ columnNames.reserve(std::size_t(columnCount));
+ for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
+ columnNames.emplace_back(sqlite3_column_origin_name(m_compiledStatement.get(), columnIndex));
+
+ return columnNames;
+}
+
+void BaseStatement::bind(int index, int value)
+{
+ int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
+ checkForBindingError(resultCode);
+}
+
+void BaseStatement::bind(int index, long long value)
+{
+ int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
+ checkForBindingError(resultCode);
+}
+
+void BaseStatement::bind(int index, double value)
+{
+ int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
+ 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);
+ checkForBindingError(resultCode);
+}
+
+template <typename Type>
+void BaseStatement::bind(Utils::SmallStringView name, Type value)
+{
+ int index = bindingIndexForName(name);
+ checkBindingName(index);
+ bind(index, value);
+}
+
+template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, int value);
+template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, long value);
+template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, long long value);
+template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, double value);
+template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text);
+
+int BaseStatement::bindingIndexForName(Utils::SmallStringView name) const
+{
+ return sqlite3_bind_parameter_index(m_compiledStatement.get(), name.data());
+}
+
+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);
+
+ checkForPrepareError(resultCode);
+}
+
+sqlite3 *BaseStatement::sqliteDatabaseHandle() const
+{
+ return m_database.backend().sqliteDatabaseHandle();
+}
+
+TextEncoding BaseStatement::databaseTextEncoding()
+{
+ return m_database.backend().textEncoding();
+}
+
+bool BaseStatement::checkForStepError(int resultCode) const
+{
+ switch (resultCode) {
+ case SQLITE_ROW: return true;
+ case SQLITE_DONE: return false;
+ 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");
+
+ Q_UNREACHABLE();
+}
+
+void BaseStatement::checkForPrepareError(int resultCode) const
+{
+ switch (resultCode) {
+ case SQLITE_OK: return;
+ 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!");
+ }
+
+ throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened");
+}
+
+void BaseStatement::checkForBindingError(int resultCode) const
+{
+ switch (resultCode) {
+ case SQLITE_OK: return;
+ 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();
+ }
+
+ throwUnknowError("SqliteStatement::bind: unknown error has happened");
+}
+
+void BaseStatement::setIfIsReadyToFetchValues(int resultCode) const
+{
+ if (resultCode == SQLITE_ROW)
+ m_isReadyToFetchValues = true;
+ else
+ m_isReadyToFetchValues = false;
+
+}
+
+void BaseStatement::checkIfIsReadyToFetchValues() const
+{
+ if (!m_isReadyToFetchValues)
+ throwNoValuesToFetch("SqliteStatement::value: there are no values to fetch!");
+}
+
+void BaseStatement::checkColumnsAreValid(const std::vector<int> &columns) const
+{
+ for (int column : columns) {
+ if (column < 0 || column >= m_columnCount)
+ throwInvalidColumnFetched("SqliteStatement::values: column index out of bound!");
+ }
+}
+
+void BaseStatement::checkColumnIsValid(int column) const
+{
+ if (column < 0 || column >= m_columnCount)
+ throwInvalidColumnFetched("SqliteStatement::values: column index out of bound!");
+}
+
+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::throwConstraintPreventsModification(const char *whatHasHappened) const
+{
+ throw ContraintPreventsModification(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
+}
+
+void BaseStatement::throwNoValuesToFetch(const char *whatHasHappened) const
+{
+ throw NoValuesToFetch(whatHasHappened);
+}
+
+void BaseStatement::throwInvalidColumnFetched(const char *whatHasHappened) const
+{
+ throw InvalidColumnFetched(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;
+}
+
+template <typename StringType>
+static StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
+{
+ const char *text = reinterpret_cast<const char*>(sqlite3_column_text(sqlStatment, column));
+ std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
+
+ return StringType(text, size);
+}
+
+template <typename StringType>
+static 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<StringType>(sqlStatment, column);
+ case SQLITE_BLOB:
+ case SQLITE_NULL: return {};
+ }
+
+ Q_UNREACHABLE();
+}
+
+int BaseStatement::fetchIntValue(int column) const
+{
+ checkIfIsReadyToFetchValues();
+ checkColumnIsValid(column);
+ return sqlite3_column_int(m_compiledStatement.get(), column);
+}
+
+template<>
+int BaseStatement::fetchValue<int>(int column) const
+{
+ return fetchIntValue(column);
+}
+
+long BaseStatement::fetchLongValue(int column) const
+{
+ return long(fetchValue<long long>(column));
+}
+
+template<>
+long BaseStatement::fetchValue<long>(int column) const
+{
+ return fetchLongValue(column);
+}
+
+long long BaseStatement::fetchLongLongValue(int column) const
+{
+ checkIfIsReadyToFetchValues();
+ checkColumnIsValid(column);
+ return sqlite3_column_int64(m_compiledStatement.get(), column);
+}
+
+template<>
+long long BaseStatement::fetchValue<long long>(int column) const
+{
+ return fetchLongLongValue(column);
+}
+
+double BaseStatement::fetchDoubleValue(int column) const
+{
+ checkIfIsReadyToFetchValues();
+ checkColumnIsValid(column);
+ return sqlite3_column_double(m_compiledStatement.get(), column);
+}
+
+template<>
+double BaseStatement::fetchValue<double>(int column) const
+{
+ return fetchDoubleValue(column);
+}
+
+template<typename StringType>
+StringType BaseStatement::fetchValue(int column) const
+{
+ checkIfIsReadyToFetchValues();
+ checkColumnIsValid(column);
+ return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+}
+
+Utils::SmallString BaseStatement::fetchSmallStringValue(int column) const
+{
+ return fetchValue<Utils::SmallString>(column);
+}
+
+Utils::PathString BaseStatement::fetchPathStringValue(int column) const
+{
+ return fetchValue<Utils::PathString>(column);
+}
+
+template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(int column) const;
+template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(int column) const;
+
+} // namespace Sqlite