diff --git a/src/libs/sqlite/createtablesqlstatementbuilder.cpp b/src/libs/sqlite/createtablesqlstatementbuilder.cpp index c543d28de396e2963e5c4229c0d08df2ed80d8d8..dfdc779faaddb868b70f504e3e3bc2755efcf549 100644 --- a/src/libs/sqlite/createtablesqlstatementbuilder.cpp +++ b/src/libs/sqlite/createtablesqlstatementbuilder.cpp @@ -28,12 +28,11 @@ namespace Sqlite { CreateTableSqlStatementBuilder::CreateTableSqlStatementBuilder() - : m_sqlStatementBuilder("CREATE TABLE IF NOT EXISTS $table($columnDefinitions)$withoutRowId"), - m_useWithoutRowId(false) + : m_sqlStatementBuilder("CREATE $temporaryTABLE $ifNotExits$table($columnDefinitions)$withoutRowId") { } -void CreateTableSqlStatementBuilder::setTable(Utils::SmallString &&tableName) +void CreateTableSqlStatementBuilder::setTableName(Utils::SmallString &&tableName) { m_sqlStatementBuilder.clear(); @@ -42,11 +41,11 @@ void CreateTableSqlStatementBuilder::setTable(Utils::SmallString &&tableName) void CreateTableSqlStatementBuilder::addColumn(Utils::SmallString &&columnName, ColumnType columnType, - IsPrimaryKey isPrimaryKey) + Contraint constraint) { m_sqlStatementBuilder.clear(); - m_columns.emplace_back(std::move(columnName), columnType, isPrimaryKey); + m_columns.emplace_back(std::move(columnName), columnType, constraint); } void CreateTableSqlStatementBuilder::setColumns(const SqliteColumns &columns) @@ -61,6 +60,16 @@ void CreateTableSqlStatementBuilder::setUseWithoutRowId(bool useWithoutRowId) m_useWithoutRowId = useWithoutRowId; } +void CreateTableSqlStatementBuilder::setUseIfNotExists(bool useIfNotExists) +{ + m_useIfNotExits = useIfNotExists; +} + +void CreateTableSqlStatementBuilder::setUseTemporaryTable(bool useTemporaryTable) +{ + m_useTemporaryTable = useTemporaryTable; +} + void CreateTableSqlStatementBuilder::clear() { m_sqlStatementBuilder.clear(); @@ -95,8 +104,11 @@ void CreateTableSqlStatementBuilder::bindColumnDefinitions() const for (const SqliteColumn &columns : m_columns) { Utils::SmallString columnDefinitionString = {columns.name(), " ", columns.typeString()}; - if (columns.isPrimaryKey()) - columnDefinitionString.append(" PRIMARY KEY"); + switch (columns.constraint()) { + case Contraint::PrimaryKey: columnDefinitionString.append(" PRIMARY KEY"); break; + case Contraint::Unique: columnDefinitionString.append(" UNIQUE"); break; + case Contraint::NoConstraint: break; + } columnDefinitionStrings.push_back(columnDefinitionString); } @@ -108,12 +120,34 @@ void CreateTableSqlStatementBuilder::bindAll() const { m_sqlStatementBuilder.bind("$table", m_tableName.clone()); + bindTemporary(); + bindIfNotExists(); bindColumnDefinitions(); + bindWithoutRowId(); +} +void CreateTableSqlStatementBuilder::bindWithoutRowId() const +{ if (m_useWithoutRowId) m_sqlStatementBuilder.bind("$withoutRowId", " WITHOUT ROWID"); else m_sqlStatementBuilder.bindEmptyText("$withoutRowId"); } +void CreateTableSqlStatementBuilder::bindIfNotExists() const +{ + if (m_useIfNotExits) + m_sqlStatementBuilder.bind("$ifNotExits", "IF NOT EXISTS "); + else + m_sqlStatementBuilder.bindEmptyText("$ifNotExits"); +} + +void CreateTableSqlStatementBuilder::bindTemporary() const +{ + if (m_useTemporaryTable) + m_sqlStatementBuilder.bind("$temporary", "TEMPORARY "); + else + m_sqlStatementBuilder.bindEmptyText("$temporary"); +} + } // namespace Sqlite diff --git a/src/libs/sqlite/createtablesqlstatementbuilder.h b/src/libs/sqlite/createtablesqlstatementbuilder.h index f33a8920a23454a384cb2232c78d29f03ee586e9..00c9ebd45a145423ed8a0a4ea99e899ada463c9b 100644 --- a/src/libs/sqlite/createtablesqlstatementbuilder.h +++ b/src/libs/sqlite/createtablesqlstatementbuilder.h @@ -35,12 +35,14 @@ class SQLITE_EXPORT CreateTableSqlStatementBuilder public: CreateTableSqlStatementBuilder(); - void setTable(Utils::SmallString &&tableName); + void setTableName(Utils::SmallString &&tableName); void addColumn(Utils::SmallString &&columnName, ColumnType columnType, - IsPrimaryKey isPrimaryKey = IsPrimaryKey::No); + Contraint constraint = Contraint::NoConstraint); void setColumns(const SqliteColumns &columns); void setUseWithoutRowId(bool useWithoutRowId); + void setUseIfNotExists(bool useIfNotExists); + void setUseTemporaryTable(bool useTemporaryTable); void clear(); void clearColumns(); @@ -52,12 +54,17 @@ public: protected: void bindColumnDefinitions() const; void bindAll() const; + void bindWithoutRowId() const; + void bindIfNotExists() const; + void bindTemporary() const; private: mutable SqlStatementBuilder m_sqlStatementBuilder; Utils::SmallString m_tableName; SqliteColumns m_columns; - bool m_useWithoutRowId; + bool m_useWithoutRowId = false; + bool m_useIfNotExits = false; + bool m_useTemporaryTable = false; }; } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitecolumn.h b/src/libs/sqlite/sqlitecolumn.h index c16333ce2317f9f20119e546df0beeb1b6177c90..e25ee68b2d7d4e9996e80c793d121415063df537 100644 --- a/src/libs/sqlite/sqlitecolumn.h +++ b/src/libs/sqlite/sqlitecolumn.h @@ -38,17 +38,17 @@ public: SqliteColumn(Utils::SmallString &&name, ColumnType type = ColumnType::Numeric, - IsPrimaryKey isPrimaryKey = IsPrimaryKey::No) + Contraint constraint = Contraint::NoConstraint) : m_name(std::move(name)), m_type(type), - m_isPrimaryKey(isPrimaryKey) + m_constraint(constraint) {} void clear() { m_name.clear(); m_type = ColumnType::Numeric; - m_isPrimaryKey = IsPrimaryKey::No; + m_constraint = Contraint::NoConstraint; } void setName(Utils::SmallString &&newName) @@ -71,14 +71,14 @@ public: return m_type; } - void setIsPrimaryKey(IsPrimaryKey isPrimaryKey) + void setContraint(Contraint constraint) { - m_isPrimaryKey = isPrimaryKey; + m_constraint = constraint; } - bool isPrimaryKey() const + Contraint constraint() const { - return m_isPrimaryKey == IsPrimaryKey::Yes; + return m_constraint; } Utils::SmallString typeString() const @@ -98,13 +98,13 @@ public: { return first.m_name == second.m_name && first.m_type == second.m_type - && first.m_isPrimaryKey == second.m_isPrimaryKey; + && first.m_constraint == second.m_constraint; } private: Utils::SmallString m_name; ColumnType m_type = ColumnType::Numeric; - IsPrimaryKey m_isPrimaryKey = IsPrimaryKey::No; + Contraint m_constraint = Contraint::NoConstraint; }; using SqliteColumns = std::vector<SqliteColumn>; diff --git a/src/libs/sqlite/sqlitedatabase.cpp b/src/libs/sqlite/sqlitedatabase.cpp index ec2379c61c1ef371d4b26c3e4cfc0afb74bc12b2..4d53eecb35a4477c0c23b9c28b527171eb93cfef 100644 --- a/src/libs/sqlite/sqlitedatabase.cpp +++ b/src/libs/sqlite/sqlitedatabase.cpp @@ -26,6 +26,7 @@ #include "sqlitedatabase.h" #include "sqlitetable.h" +#include "sqlitetransaction.h" namespace Sqlite { @@ -34,6 +35,12 @@ SqliteDatabase::SqliteDatabase() { } +SqliteDatabase::SqliteDatabase(Utils::PathString &&databaseFilePath) + : m_databaseBackend(*this) +{ + open(std::move(databaseFilePath)); +} + void SqliteDatabase::open() { m_databaseBackend.open(m_databaseFilePath, m_openMode); @@ -61,7 +68,7 @@ bool SqliteDatabase::isOpen() const SqliteTable &SqliteDatabase::addTable() { - m_sqliteTables.emplace_back(*this); + m_sqliteTables.emplace_back(); return m_sqliteTables.back(); } @@ -118,8 +125,12 @@ void SqliteDatabase::execute(Utils::SmallStringView sqlStatement) void SqliteDatabase::initializeTables() { + SqliteImmediateTransaction<SqliteDatabase> transaction(*this); + for (SqliteTable &table : m_sqliteTables) - table.initialize(); + table.initialize(*this); + + transaction.commit(); } SqliteDatabaseBackend &SqliteDatabase::backend() diff --git a/src/libs/sqlite/sqlitedatabase.h b/src/libs/sqlite/sqlitedatabase.h index 2e178a0190cb3b209f7069b987b96a4b206f0861..0ac340bb12946e49f987d59eaed24edd76277c63 100644 --- a/src/libs/sqlite/sqlitedatabase.h +++ b/src/libs/sqlite/sqlitedatabase.h @@ -37,12 +37,14 @@ namespace Sqlite { class SQLITE_EXPORT SqliteDatabase { + template <typename Database> friend class SqliteAbstractTransaction; friend class SqliteStatement; friend class SqliteBackend; public: SqliteDatabase(); + SqliteDatabase(Utils::PathString &&databaseFilePath); SqliteDatabase(const SqliteDatabase &) = delete; bool operator=(const SqliteDatabase &) = delete; diff --git a/src/libs/sqlite/sqlitedatabasebackend.cpp b/src/libs/sqlite/sqlitedatabasebackend.cpp index 79eed32eab4d99175e621e1a01cc34c4c5bf5555..6ea70f30ada216b8bc1a34fbb651969426749a45 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.cpp +++ b/src/libs/sqlite/sqlitedatabasebackend.cpp @@ -38,14 +38,6 @@ #include "sqlite3.h" -#if defined(Q_OS_DARWIN) && defined(Q_CC_GNU) -#define QTC_THREAD_LOCAL __thread -#else -#define QTC_THREAD_LOCAL thread_local -#endif - -#define SIZE_OF_BYTEARRAY_ARRAY(array) sizeof(array)/sizeof(QByteArray) - namespace Sqlite { SqliteDatabaseBackend::SqliteDatabaseBackend(SqliteDatabase &database) diff --git a/src/libs/sqlite/sqliteglobal.h b/src/libs/sqlite/sqliteglobal.h index cb5208df5ff24ef822547dea19d4ca676f0eb6c0..af9d9ac36c0aaff2c9060a3687c0e3a76bd62b5d 100644 --- a/src/libs/sqlite/sqliteglobal.h +++ b/src/libs/sqlite/sqliteglobal.h @@ -37,6 +37,8 @@ # define SQLITE_EXPORT Q_DECL_IMPORT #endif +namespace Sqlite { + enum class ColumnType : char { Numeric, @@ -46,10 +48,11 @@ enum class ColumnType : char None }; -enum class IsPrimaryKey : char +enum class Contraint : char { - No, - Yes + NoConstraint, + PrimaryKey, + Unique }; enum class ColumnConstraint : char @@ -84,3 +87,5 @@ enum TextEncoding : char #endif }; + +} // namespace Sqlite diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 55bd5c15fbb991668963cbb38aa8b0eea4e70305..d0c8860281124b0fd3d1e194e94a18b3a120a5dd 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -37,6 +37,8 @@ public: using SqliteStatement::next; using SqliteStatement::reset; using SqliteStatement::value; + using SqliteStatement::structValues; + using SqliteStatement::tupleValues; using SqliteStatement::text; using SqliteStatement::values; using SqliteStatement::columnCount; diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 8f93ff85bed073f991eb10d4576c89c3c1573873..d53614bcc02721cce9cbe1be2b3b748f850cd830 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -48,6 +48,8 @@ public: using SqliteStatement::value; using SqliteStatement::text; using SqliteStatement::values; + using SqliteStatement::structValues; + using SqliteStatement::tupleValues; using SqliteStatement::columnCount; using SqliteStatement::columnNames; using SqliteStatement::toValue; diff --git a/src/libs/sqlite/sqlitestatement.cpp b/src/libs/sqlite/sqlitestatement.cpp index fee0130acc7d1fbdbba56b2aeba7c934ef0afa6e..4c4a458a968aaba7ae37b583440845457d9d9460 100644 --- a/src/libs/sqlite/sqlitestatement.cpp +++ b/src/libs/sqlite/sqlitestatement.cpp @@ -29,13 +29,11 @@ #include "sqlitedatabasebackend.h" #include "sqliteexception.h" -#include <QMutex> -#include <QtGlobal> -#include <QVariant> -#include <QWaitCondition> - #include "sqlite3.h" +#include <condition_variable> +#include <mutex> + #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif @@ -61,8 +59,8 @@ void SqliteStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement) sqlite3_finalize(compiledStatement); } -class UnlockNotification { - +class UnlockNotification +{ public: static void unlockNotifyCallBack(void **arguments, int argumentCount) { @@ -74,27 +72,24 @@ public: void wakeupWaitCondition() { - mutex.lock(); - fired = 1; - waitCondition.wakeAll(); - mutex.unlock(); + { + std::lock_guard<std::mutex> lock(m_mutex); + m_fired = 1; + } + m_waitCondition.notify_all(); } void wait() { - mutex.lock(); + std::unique_lock<std::mutex> lock(m_mutex); - if (!fired) { - waitCondition.wait(&mutex); - } - - mutex.unlock(); + m_waitCondition.wait(lock, [&] () { return m_fired; }); } private: - bool fired = false; - QWaitCondition waitCondition; - QMutex mutex; + bool m_fired = false; + std::condition_variable m_waitCondition; + std::mutex m_mutex; }; void SqliteStatement::waitForUnlockNotify() const @@ -143,6 +138,7 @@ void SqliteStatement::step() const void SqliteStatement::execute() const { next(); + reset(); } int SqliteStatement::columnCount() const @@ -168,7 +164,7 @@ void SqliteStatement::bind(int index, int value) throwException("SqliteStatement::bind: cant' bind 32 bit integer!"); } -void SqliteStatement::bind(int index, qint64 value) +void SqliteStatement::bind(int index, long long value) { int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -198,7 +194,8 @@ void SqliteStatement::bind(Utils::SmallStringView name, Type value) } template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, int value); -template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, qint64 value); +template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, long value); +template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, long long value); template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, double value); template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text); @@ -363,21 +360,23 @@ SqliteDatabase &SqliteStatement::database() const return m_database; } -static Utils::SmallString textForColumn(sqlite3_stmt *sqlStatment, int column) +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 Utils::SmallString(text, size); + return StringType(text, size); } -static Utils::SmallString convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) +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(sqlStatment, column); + case SQLITE3_TEXT: return textForColumn<StringType>(sqlStatment, column); case SQLITE_BLOB: case SQLITE_NULL: return {}; } @@ -394,7 +393,13 @@ int SqliteStatement::value<int>(int column) const } template<> -qint64 SqliteStatement::value<qint64>(int column) const +long SqliteStatement::value<long>(int column) const +{ + return long(value<long long>(column)); +} + +template<> +long long SqliteStatement::value<long long>(int column) const { checkIfIsReadyToFetchValues(); checkColumnIsValid(column); @@ -409,14 +414,17 @@ double SqliteStatement::value<double>(int column) const return sqlite3_column_double(m_compiledStatement.get(), column); } -template<> -Utils::SmallString SqliteStatement::value<Utils::SmallString>(int column) const +template<typename StringType> +StringType SqliteStatement::value(int column) const { checkIfIsReadyToFetchValues(); checkColumnIsValid(column); - return convertToTextForColumn(m_compiledStatement.get(), column); + return convertToTextForColumn<StringType>(m_compiledStatement.get(), column); } +template SQLITE_EXPORT Utils::SmallString SqliteStatement::value<Utils::SmallString>(int column) const; +template SQLITE_EXPORT Utils::PathString SqliteStatement::value<Utils::PathString>(int column) const; + Utils::SmallString SqliteStatement::text(int column) const { return value<Utils::SmallString>(column); @@ -434,45 +442,6 @@ ContainerType SqliteStatement::columnValues(const std::vector<int> &columnIndice return valueContainer; } -template <typename ContainerType> -ContainerType SqliteStatement::values(const std::vector<int> &columns, int size) const -{ - checkColumnsAreValid(columns); - - ContainerType resultValues; - resultValues.reserve(typename ContainerType::size_type(size)); - - reset(); - - while (next()) { - auto values = columnValues<ContainerType>(columns); - std::move(values.begin(), values.end(), std::back_inserter(resultValues)); - } - - return resultValues; -} - -template SQLITE_EXPORT Utils::SmallStringVector SqliteStatement::values<Utils::SmallStringVector>(const std::vector<int> &columnIndices, int size) const; - -template <typename ContainerType> -ContainerType SqliteStatement::values(int column) const -{ - typedef typename ContainerType::value_type ElementType; - ContainerType resultValues; - - reset(); - - while (next()) { - resultValues.push_back(value<ElementType>(column)); - } - - return resultValues; -} - -template SQLITE_EXPORT std::vector<qint64> SqliteStatement::values<std::vector<qint64>>(int column) const; -template SQLITE_EXPORT std::vector<double> SqliteStatement::values<std::vector<double>>(int column) const; -template SQLITE_EXPORT Utils::SmallStringVector SqliteStatement::values<Utils::SmallStringVector>(int column) const; - template <typename Type> Type SqliteStatement::toValue(Utils::SmallStringView sqlStatement, SqliteDatabase &database) { @@ -484,7 +453,7 @@ Type SqliteStatement::toValue(Utils::SmallStringView sqlStatement, SqliteDatabas } template SQLITE_EXPORT int SqliteStatement::toValue<int>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); -template SQLITE_EXPORT qint64 SqliteStatement::toValue<qint64>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); +template SQLITE_EXPORT long long SqliteStatement::toValue<long long>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); template SQLITE_EXPORT double SqliteStatement::toValue<double>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); template SQLITE_EXPORT Utils::SmallString SqliteStatement::toValue<Utils::SmallString>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); diff --git a/src/libs/sqlite/sqlitestatement.h b/src/libs/sqlite/sqlitestatement.h index 5f74c267ef380f05f79ed0d871db1193d770313b..09c75c16d974055e0f38a820fc8b23f7d74f79e4 100644 --- a/src/libs/sqlite/sqlitestatement.h +++ b/src/libs/sqlite/sqlitestatement.h @@ -31,8 +31,12 @@ #include <utils/smallstringvector.h> -#include <type_traits> +#include <cstdint> #include <memory> +#include <type_traits> +#include <tuple> + +using std::int64_t; struct sqlite3_stmt; struct sqlite3; @@ -61,31 +65,45 @@ protected: Utils::SmallStringVector columnNames() const; void bind(int index, int value); - void bind(int index, qint64 value); + void bind(int index, long long value); void bind(int index, double value); void bind(int index, Utils::SmallStringView value); - template<typename ... Values> - void bindValues(Values ... values) + void bind(int index, uint value) + { + bind(index, static_cast<long long>(value)); + } + + void bind(int index, long value) + { + bind(index, static_cast<long long>(value)); + } + + void bindValues() + { + } + + template<typename... Values> + void bindValues(Values... values) { bindValuesByIndex(1, values...); } - template<typename ... Values> - void write(Values ... values) + template<typename... Values> + void write(Values... values) { bindValuesByIndex(1, values...); execute(); } - template<typename ... Values> - void bindNameValues(Values ... values) + template<typename... Values> + void bindNameValues(Values... values) { bindValuesByName(values...); } - template<typename ... Values> - void writeNamed(Values ... values) + template<typename... Values> + void writeNamed(Values... values) { bindValuesByName(values...); execute(); @@ -99,11 +117,231 @@ protected: void setBindingColumnNames(const Utils::SmallStringVector &bindingColumnNames); const Utils::SmallStringVector &bindingColumnNames() const; - template <typename ContainerType> - ContainerType values(const std::vector<int> &columns, int size = 0) const; + template <typename... ResultType> + std::vector<std::tuple<ResultType...>> tupleValues(std::size_t reserveSize) + { + using Container = std::vector<std::tuple<ResultType...>>; + Container resultValues; + resultValues.reserve(reserveSize); - template <typename ContainerType> - ContainerType values(int column = 0) const; + while (next()) + emplaceTupleValues<Container, ResultType...>(resultValues); + + reset(); + + return resultValues; + } + + template <typename... ResultType, + typename... QueryType> + std::vector<std::tuple<ResultType...>> tupleValues(std::size_t reserveSize, const QueryType&... queryValues) + { + using Container = std::vector<std::tuple<ResultType...>>; + Container resultValues; + resultValues.reserve(reserveSize); + + bindValues(queryValues...); + + while (next()) + emplaceTupleValues<Container, ResultType...>(resultValues); + + reset(); + + return resultValues; + } + + template <typename... ResultType, + typename... ElementType> + std::vector<std::tuple<ResultType...>> tupleValues(std::size_t reserveSize, + const std::vector<std::tuple<ElementType...>> &queryTuples) + { + using Container = std::vector<std::tuple<ResultType...>>; + Container resultValues; + resultValues.reserve(reserveSize); + + for (const auto &queryTuple : queryTuples) { + bindTupleValues(queryTuple); + + while (next()) + emplaceTupleValues<Container, ResultType...>(resultValues); + + reset(); + } + + return resultValues; + } + + template <typename... ResultType, + typename QueryElementType> + std::vector<std::tuple<ResultType...>> tupleValues(std::size_t reserveSize, + const std::vector<QueryElementType> &queryValues) + { + using Container = std::vector<std::tuple<ResultType...>>; + Container resultValues; + resultValues.reserve(reserveSize); + + for (const QueryElementType &queryValue : queryValues) { + bindValues(queryValue); + + while (next()) + emplaceTupleValues<Container, ResultType...>(resultValues); + + reset(); + } + + return resultValues; + } + + template <typename ResultType, + typename... ResultEntryType> + std::vector<ResultType> structValues(std::size_t reserveSize) + { + using Container = std::vector<ResultType>; + Container resultValues; + resultValues.reserve(reserveSize); + + while (next()) + pushBackStructValues<Container, ResultEntryType...>(resultValues); + + reset(); + + return resultValues; + } + + template <typename ResultType, + typename... ResultEntryType, + typename... QueryType> + std::vector<ResultType> structValues(std::size_t reserveSize, const QueryType&... queryValues) + { + using Container = std::vector<ResultType>; + Container resultValues; + resultValues.reserve(reserveSize); + + bindValues(queryValues...); + + while (next()) + pushBackStructValues<Container, ResultEntryType...>(resultValues); + + reset(); + + return resultValues; + } + + template <typename ResultType, + typename... ResultEntryType, + typename QueryElementType> + std::vector<ResultType> structValues(std::size_t reserveSize, + const std::vector<QueryElementType> &queryValues) + { + using Container = std::vector<ResultType>; + Container resultValues; + resultValues.reserve(reserveSize); + + for (const QueryElementType &queryValue : queryValues) { + bindValues(queryValue); + + while (next()) + pushBackStructValues<Container, ResultEntryType...>(resultValues); + + reset(); + } + + return resultValues; + } + + template <typename ResultType, + typename... ResultEntryType, + typename... QueryElementType> + std::vector<ResultType> structValues(std::size_t reserveSize, + const std::vector<std::tuple<QueryElementType...>> &queryTuples) + { + using Container = std::vector<ResultType>; + Container resultValues; + resultValues.reserve(reserveSize); + + for (const auto &queryTuple : queryTuples) { + bindTupleValues(queryTuple); + + while (next()) + pushBackStructValues<Container, ResultEntryType...>(resultValues); + + reset(); + } + + return resultValues; + } + + template <typename ResultType, + typename... ElementType> + std::vector<ResultType> values(std::size_t reserveSize) + { + std::vector<ResultType> resultValues; + resultValues.reserve(reserveSize); + + while (next()) + resultValues.push_back(value<ResultType>(0)); + + reset(); + + return resultValues; + } + + template <typename ResultType, + typename... ElementType> + std::vector<ResultType> values(std::size_t reserveSize, + const std::vector<std::tuple<ElementType...>> &queryTuples) + { + std::vector<ResultType> resultValues; + resultValues.reserve(reserveSize); + + for (const auto &queryTuple : queryTuples) { + bindTupleValues(queryTuple); + + while (next()) + resultValues.push_back(value<ResultType>(0)); + + reset(); + } + + return resultValues; + } + + template <typename ResultType, + typename ElementType> + std::vector<ResultType> values(std::size_t reserveSize, + const std::vector<ElementType> &queryValues) + { + std::vector<ResultType> resultValues; + resultValues.reserve(reserveSize); + + for (const ElementType &queryValue : queryValues) { + bindValues(queryValue); + + while (next()) + resultValues.push_back(value<ResultType>(0)); + + reset(); + } + + return resultValues; + } + + template <typename ResultType, + typename... QueryType> + std::vector<ResultType> values(std::size_t reserveSize, const QueryType&... queryValues) + { + std::vector<ResultType> resultValues; + resultValues.reserve(reserveSize); + + bindValues(queryValues...); + + while (next()) + resultValues.push_back(value<ResultType>(0)); + + reset(); + + return resultValues; + } template <typename Type> static Type toValue(Utils::SmallStringView sqlStatement, SqliteDatabase &database); @@ -141,14 +379,45 @@ protected: SqliteDatabaseBackend &databaseBackend); private: + template <typename Container, + typename... ResultType, + int... Index> + void emplaceTupleValues(Container &container, std::integer_sequence<int, Index...>) + { + container.emplace_back(value<ResultType>(Index)...); + } + + template <typename Container, + typename... ResultType> + void emplaceTupleValues(Container &container) + { + emplaceTupleValues<Container, ResultType...>(container, std::make_integer_sequence<int, sizeof...(ResultType)>{}); + } + + template <typename Container, + typename... ResultEntryType, + int... Index> + void pushBackStructValues(Container &container, std::integer_sequence<int, Index...>) + { + using ResultType = typename Container::value_type; + container.push_back(ResultType{value<ResultEntryType>(Index)...}); + } + + template <typename Container, + typename... ResultEntryType> + void pushBackStructValues(Container &container) + { + pushBackStructValues<Container, ResultEntryType...>(container, std::make_integer_sequence<int, sizeof...(ResultEntryType)>{}); + } + template<typename Type> void bindValuesByIndex(int index, Type value) { bind(index, value); } - template<typename Type, typename ... Value> - void bindValuesByIndex(int index, Type value, Value ... values) + template<typename Type, typename... Value> + void bindValuesByIndex(int index, Type value, Value... values) { bind(index, value); bindValuesByIndex(index + 1, values...); @@ -160,13 +429,26 @@ private: bind(bindingIndexForName(name), value); } - template<typename Type, typename ... Values> - void bindValuesByName(Utils::SmallStringView name, Type value, Values ... values) + template<typename Type, typename... Values> + void bindValuesByName(Utils::SmallStringView name, Type value, Values... values) { bind(bindingIndexForName(name), value); bindValuesByName(values...); } + template <typename TupleType, std::size_t... Index> + void bindTupleValuesElement(const TupleType &tuple, std::index_sequence<Index...>) + { + bindValues(std::get<Index>(tuple)...); + } + + template <typename TupleType, + typename Indices = std::make_index_sequence<std::tuple_size<TupleType>::value>> + void bindTupleValues(const TupleType &element) + { + bindTupleValuesElement(element, Indices()); + } + private: std::unique_ptr<sqlite3_stmt, void (*)(sqlite3_stmt*)> m_compiledStatement; Utils::SmallStringVector m_bindingColumnNames; @@ -176,4 +458,21 @@ private: mutable bool m_isReadyToFetchValues; }; +extern template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, int value); +extern template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, long value); +extern template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, long long value); +extern template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, double value); +extern template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text); + +extern template SQLITE_EXPORT int SqliteStatement::toValue<int>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); +extern template SQLITE_EXPORT long long SqliteStatement::toValue<long long>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); +extern template SQLITE_EXPORT double SqliteStatement::toValue<double>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); +extern template SQLITE_EXPORT Utils::SmallString SqliteStatement::toValue<Utils::SmallString>(Utils::SmallStringView sqlStatement, SqliteDatabase &database); + +template <> SQLITE_EXPORT int SqliteStatement::value<int>(int column) const; +template <> SQLITE_EXPORT long SqliteStatement::value<long>(int column) const; +template <> SQLITE_EXPORT long long SqliteStatement::value<long long>(int column) const; +template <> SQLITE_EXPORT double SqliteStatement::value<double>(int column) const; +extern template SQLITE_EXPORT Utils::SmallString SqliteStatement::value<Utils::SmallString>(int column) const; +extern template SQLITE_EXPORT Utils::PathString SqliteStatement::value<Utils::PathString>(int column) const; } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitetable.cpp b/src/libs/sqlite/sqlitetable.cpp index dc987f4477276643c0aed11719b2efdbc37c38fe..99b0e95938cdb7205b89548948aec59f146533f1 100644 --- a/src/libs/sqlite/sqlitetable.cpp +++ b/src/libs/sqlite/sqlitetable.cpp @@ -25,32 +25,8 @@ #include "sqlitetable.h" -#include "sqlitecolumn.h" -#include "sqlitedatabase.h" -#include "createtablesqlstatementbuilder.h" -#include "sqlitewritestatement.h" -#include "sqlitetransaction.h" - namespace Sqlite { -void SqliteTable::initialize() -{ - try { - CreateTableSqlStatementBuilder createTableSqlStatementBuilder; - - createTableSqlStatementBuilder.setTable(m_tableName.clone()); - createTableSqlStatementBuilder.setUseWithoutRowId(m_withoutRowId); - createTableSqlStatementBuilder.setColumns(m_sqliteColumns); - - SqliteImmediateTransaction transaction(m_sqliteDatabase); - m_sqliteDatabase.execute(createTableSqlStatementBuilder.sqlStatement()); - transaction.commit(); - - m_isReady = true; - } catch (const SqliteException &exception) { - exception.printWarning(); - } -} } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitetable.h b/src/libs/sqlite/sqlitetable.h index 62b6b41cb3fe07a3eb8cc7f3560ee06710019fda..d591f39a59f8dd10975581e86646da2debe57639 100644 --- a/src/libs/sqlite/sqlitetable.h +++ b/src/libs/sqlite/sqlitetable.h @@ -25,8 +25,10 @@ #pragma once +#include "createtablesqlstatementbuilder.h" #include "sqliteglobal.h" #include "sqlitecolumn.h" +#include "sqliteexception.h" namespace Sqlite { @@ -35,11 +37,6 @@ class SqliteDatabase; class SqliteTable { public: - SqliteTable(SqliteDatabase &m_sqliteDatabase) - : m_sqliteDatabase(m_sqliteDatabase) - { - } - void setName(Utils::SmallString &&name) { m_tableName = std::move(name); @@ -60,11 +57,16 @@ public: return m_withoutRowId; } + void setUseIfNotExists(bool useIfNotExists) + { + m_useIfNotExists = useIfNotExists; + } + SqliteColumn &addColumn(Utils::SmallString &&name, ColumnType type = ColumnType::Numeric, - IsPrimaryKey isPrimaryKey = IsPrimaryKey::No) + Contraint constraint = Contraint::NoConstraint) { - m_sqliteColumns.emplace_back(std::move(name), type, isPrimaryKey); + m_sqliteColumns.emplace_back(std::move(name), type, constraint); return m_sqliteColumns.back(); } @@ -79,13 +81,31 @@ public: return m_isReady; } - void initialize(); + template <typename Database> + void initialize(Database &database) + { + try { + CreateTableSqlStatementBuilder builder; + + builder.setTableName(m_tableName.clone()); + builder.setUseWithoutRowId(m_withoutRowId); + builder.setUseIfNotExists(m_useIfNotExists); + builder.setColumns(m_sqliteColumns); + + database.execute(builder.sqlStatement()); + + m_isReady = true; + + } catch (const SqliteException &exception) { + exception.printWarning(); + } + } friend bool operator==(const SqliteTable &first, const SqliteTable &second) { return first.m_tableName == second.m_tableName - && &first.m_sqliteDatabase == &second.m_sqliteDatabase && first.m_withoutRowId == second.m_withoutRowId + && first.m_useIfNotExists == second.m_useIfNotExists && first.m_isReady == second.m_isReady && first.m_sqliteColumns == second.m_sqliteColumns; } @@ -93,8 +113,8 @@ public: private: Utils::SmallString m_tableName; SqliteColumns m_sqliteColumns; - SqliteDatabase &m_sqliteDatabase; bool m_withoutRowId = false; + bool m_useIfNotExists = false; bool m_isReady = false; }; diff --git a/src/libs/sqlite/sqlitetransaction.cpp b/src/libs/sqlite/sqlitetransaction.cpp index dd790ee55296b8d4c97abc1482a6b80be17d665c..2ee17131c25fc53dbbf94fa7da1a48004e6e98cb 100644 --- a/src/libs/sqlite/sqlitetransaction.cpp +++ b/src/libs/sqlite/sqlitetransaction.cpp @@ -31,44 +31,4 @@ namespace Sqlite { -SqliteAbstractTransaction::~SqliteAbstractTransaction() -{ - if (!m_isAlreadyCommited) - m_databaseBackend.execute("ROLLBACK"); -} - -void SqliteAbstractTransaction::commit() -{ - m_databaseBackend.execute("COMMIT"); - m_isAlreadyCommited = true; -} - -SqliteAbstractTransaction::SqliteAbstractTransaction(SqliteDatabaseBackend &backend) - : m_databaseBackend(backend) -{ -} - -SqliteAbstractTransaction::SqliteAbstractTransaction(SqliteDatabase &database) - : SqliteAbstractTransaction(database.backend()) -{ -} - -SqliteTransaction::SqliteTransaction(SqliteDatabase &database) - : SqliteAbstractTransaction(database) -{ - m_databaseBackend.execute("BEGIN"); -} - -SqliteImmediateTransaction::SqliteImmediateTransaction(SqliteDatabase &database) - : SqliteAbstractTransaction(database) -{ - m_databaseBackend.execute("BEGIN IMMEDIATE"); -} - -SqliteExclusiveTransaction::SqliteExclusiveTransaction(SqliteDatabase &database) - : SqliteAbstractTransaction(database) -{ - m_databaseBackend.execute("BEGIN EXCLUSIVE"); -} - } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitetransaction.h b/src/libs/sqlite/sqlitetransaction.h index 994fbe2b1f5c0003ae3afed79ab1e49e5dfb73e9..373b33fd8a928de6044f773c634549fa771fc32a 100644 --- a/src/libs/sqlite/sqlitetransaction.h +++ b/src/libs/sqlite/sqlitetransaction.h @@ -32,43 +32,66 @@ namespace Sqlite { class SqliteDatabaseBackend; class SqliteDatabase; -class SQLITE_EXPORT SqliteAbstractTransaction +template <typename Database> +class SqliteAbstractTransaction { public: - virtual ~SqliteAbstractTransaction(); - - void commit(); - -protected: - SqliteAbstractTransaction(SqliteDatabaseBackend &backend); - SqliteAbstractTransaction(SqliteDatabase &database); + virtual ~SqliteAbstractTransaction() + { + if (!m_isAlreadyCommited) + m_database.execute("ROLLBACK"); + } + + void commit() + { + m_database.execute("COMMIT"); + m_isAlreadyCommited = true; + } protected: - SqliteDatabaseBackend &m_databaseBackend; + SqliteAbstractTransaction(Database &database) + : m_database(database) + { + } private: + Database &m_database; bool m_isAlreadyCommited = false; }; - -class SQLITE_EXPORT SqliteTransaction final : public SqliteAbstractTransaction +template <typename Database> +class SqliteTransaction final : public SqliteAbstractTransaction<Database> { public: - SqliteTransaction(SqliteDatabase &database); + SqliteTransaction(Database &database) + : SqliteAbstractTransaction<Database>(database) + { + database.execute("BEGIN"); + } }; -class SQLITE_EXPORT SqliteImmediateTransaction final : public SqliteAbstractTransaction +template <typename Database> +class SqliteImmediateTransaction final : public SqliteAbstractTransaction<Database> { public: - SqliteImmediateTransaction(SqliteDatabase &database); + SqliteImmediateTransaction(Database &database) + : SqliteAbstractTransaction<Database>(database) + { + database.execute("BEGIN IMMEDIATE"); + } }; -class SQLITE_EXPORT SqliteExclusiveTransaction final : public SqliteAbstractTransaction +template <typename Database> +class SqliteExclusiveTransaction final : public SqliteAbstractTransaction<Database> { public: - SqliteExclusiveTransaction(SqliteDatabase &database); + SqliteExclusiveTransaction(Database &database) + : SqliteAbstractTransaction<Database>(database) + { + database.execute("BEGIN EXCLUSIVE"); + } }; diff --git a/src/libs/sqlite/sqlstatementbuilder.cpp b/src/libs/sqlite/sqlstatementbuilder.cpp index 4104d8c1f0c4fb53d328a7936ae255c7e3d24b4d..cfa08a2b7b360e59e12892abbc5d5d9ce1cfdb3a 100644 --- a/src/libs/sqlite/sqlstatementbuilder.cpp +++ b/src/libs/sqlite/sqlstatementbuilder.cpp @@ -33,7 +33,7 @@ namespace Sqlite { -SqlStatementBuilder::SqlStatementBuilder(Utils::SmallString &&sqlTemplate) +SqlStatementBuilder::SqlStatementBuilder(Utils::SmallStringView sqlTemplate) : m_sqlTemplate(std::move(sqlTemplate)) { } diff --git a/src/libs/sqlite/sqlstatementbuilder.h b/src/libs/sqlite/sqlstatementbuilder.h index c24e0b9ed947dd20f766d70a17252a4038185154..670455dc41a5764fff8cdb7518fbe46a42825864 100644 --- a/src/libs/sqlite/sqlstatementbuilder.h +++ b/src/libs/sqlite/sqlstatementbuilder.h @@ -38,7 +38,7 @@ class SQLITE_EXPORT SqlStatementBuilder { using BindingPair = std::pair<Utils::SmallString, Utils::SmallString>; public: - SqlStatementBuilder(Utils::SmallString &&m_sqlTemplate); + SqlStatementBuilder(Utils::SmallStringView m_sqlTemplate); void bindEmptyText(Utils::SmallString &&name); void bind(Utils::SmallString &&name, Utils::SmallString &&text); @@ -79,8 +79,8 @@ protected: Q_NORETURN static void throwException(const char *whatHasHappened, const char *errorMessage); private: - Utils::SmallString m_sqlTemplate; - mutable Utils::SmallString m_sqlStatement; + Utils::BasicSmallString<510> m_sqlTemplate; + mutable Utils::BasicSmallString<510> m_sqlStatement; mutable std::vector<BindingPair> m_bindings; }; diff --git a/tests/unit/unittest/createtablesqlstatementbuilder-test.cpp b/tests/unit/unittest/createtablesqlstatementbuilder-test.cpp index faea6c850dacb1fc1936bf6b7148b3a8a4367f03..1114403ac66ca4ede8f5a6025817361cf26b490c 100644 --- a/tests/unit/unittest/createtablesqlstatementbuilder-test.cpp +++ b/tests/unit/unittest/createtablesqlstatementbuilder-test.cpp @@ -30,6 +30,10 @@ namespace { +using Sqlite::ColumnType; +using Sqlite::Contraint; +using Sqlite::JournalMode; +using Sqlite::OpenMode; using Sqlite::SqliteColumn; using Sqlite::SqliteColumns; @@ -81,7 +85,7 @@ TEST_F(CreateTableSqlStatementBuilder, SqlStatement) bindValues(); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)"); + "CREATE TABLE test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)"); } TEST_F(CreateTableSqlStatementBuilder, AddColumnToExistingColumns) @@ -91,17 +95,17 @@ TEST_F(CreateTableSqlStatementBuilder, AddColumnToExistingColumns) builder.addColumn("number2", ColumnType::Real); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC, number2 REAL)"); + "CREATE TABLE test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC, number2 REAL)"); } TEST_F(CreateTableSqlStatementBuilder, ChangeTable) { bindValues(); - builder.setTable("test2"); + builder.setTableName("test2"); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test2(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)" + "CREATE TABLE test2(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)" ); } @@ -124,7 +128,7 @@ TEST_F(CreateTableSqlStatementBuilder, ClearColumnsAndAddColumnNewColumns) builder.addColumn("number3", ColumnType::Real); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test(name3 TEXT, number3 REAL)"); + "CREATE TABLE test(name3 TEXT, number3 REAL)"); } TEST_F(CreateTableSqlStatementBuilder, SetWitoutRowId) @@ -134,25 +138,60 @@ TEST_F(CreateTableSqlStatementBuilder, SetWitoutRowId) builder.setUseWithoutRowId(true); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC) WITHOUT ROWID"); + "CREATE TABLE test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC) WITHOUT ROWID"); } TEST_F(CreateTableSqlStatementBuilder, SetColumnDefinitions) { builder.clear(); - builder.setTable("test"); + builder.setTableName("test"); builder.setColumns(createColumns()); ASSERT_THAT(builder.sqlStatement(), - "CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)"); + "CREATE TABLE test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)"); +} + +TEST_F(CreateTableSqlStatementBuilder, UniqueContraint) +{ + builder.clear(); + builder.setTableName("test"); + + builder.addColumn("id", ColumnType::Integer, Contraint::Unique); + + ASSERT_THAT(builder.sqlStatement(), + "CREATE TABLE test(id INTEGER UNIQUE)"); +} + +TEST_F(CreateTableSqlStatementBuilder, IfNotExitsModifier) +{ + builder.clear(); + builder.setTableName("test"); + builder.addColumn("id", ColumnType::Integer, Contraint::NoConstraint); + + builder.setUseIfNotExists(true); + + ASSERT_THAT(builder.sqlStatement(), + "CREATE TABLE IF NOT EXISTS test(id INTEGER)"); +} + +TEST_F(CreateTableSqlStatementBuilder, TemporaryTable) +{ + builder.clear(); + builder.setTableName("test"); + builder.addColumn("id", ColumnType::Integer, Contraint::NoConstraint); + + builder.setUseTemporaryTable(true); + + ASSERT_THAT(builder.sqlStatement(), + "CREATE TEMPORARY TABLE test(id INTEGER)"); } void CreateTableSqlStatementBuilder::bindValues() { builder.clear(); - builder.setTable("test"); - builder.addColumn("id", ColumnType::Integer, IsPrimaryKey::Yes); + builder.setTableName("test"); + builder.addColumn("id", ColumnType::Integer, Contraint::PrimaryKey); builder.addColumn("name", ColumnType::Text); builder.addColumn("number",ColumnType:: Numeric); } @@ -160,7 +199,7 @@ void CreateTableSqlStatementBuilder::bindValues() SqliteColumns CreateTableSqlStatementBuilder::createColumns() { SqliteColumns columns; - columns.emplace_back("id", ColumnType::Integer, IsPrimaryKey::Yes); + columns.emplace_back("id", ColumnType::Integer, Contraint::PrimaryKey); columns.emplace_back("name", ColumnType::Text); columns.emplace_back("number", ColumnType::Numeric); diff --git a/tests/unit/unittest/sqlitecolumn-test.cpp b/tests/unit/unittest/sqlitecolumn-test.cpp index a7778cc2107e7d84302f05d31d6177e12600e115..c850fcccae86592ed1799c828d73ef7da28281b6 100644 --- a/tests/unit/unittest/sqlitecolumn-test.cpp +++ b/tests/unit/unittest/sqlitecolumn-test.cpp @@ -33,6 +33,10 @@ using testing::AllOf; using testing::Contains; using testing::Property; +using Sqlite::ColumnType; +using Sqlite::Contraint; +using Sqlite::JournalMode; +using Sqlite::OpenMode; using Column = Sqlite::SqliteColumn; using Sqlite::SqliteColumns; @@ -63,16 +67,16 @@ TEST_F(SqliteColumn, ChangeType) ASSERT_THAT(column.type(), ColumnType::Text); } -TEST_F(SqliteColumn, DefaultPrimaryKey) +TEST_F(SqliteColumn, DefaultConstraint) { - ASSERT_FALSE(column.isPrimaryKey()); + ASSERT_THAT(column.constraint(), Contraint::NoConstraint); } -TEST_F(SqliteColumn, SetPrimaryKey) +TEST_F(SqliteColumn, SetConstraint) { - column.setIsPrimaryKey(IsPrimaryKey::Yes); + column.setContraint(Contraint::PrimaryKey); - ASSERT_TRUE(column.isPrimaryKey()); + ASSERT_THAT(column.constraint(), Contraint::PrimaryKey); } TEST_F(SqliteColumn, GetColumnDefinition) @@ -83,7 +87,7 @@ TEST_F(SqliteColumn, GetColumnDefinition) AllOf( Property(&Column::name, "Claudia"), Property(&Column::type, ColumnType::Numeric), - Property(&Column::isPrimaryKey, false))); + Property(&Column::constraint, Contraint::NoConstraint))); } void SqliteColumn::SetUp() diff --git a/tests/unit/unittest/sqlitedatabase-test.cpp b/tests/unit/unittest/sqlitedatabase-test.cpp index 59d9467246bb96ca2ff29aed8218b9da04fa1c39..85b8a68e80904c6592923eb0e83d29da183e25e4 100644 --- a/tests/unit/unittest/sqlitedatabase-test.cpp +++ b/tests/unit/unittest/sqlitedatabase-test.cpp @@ -38,6 +38,9 @@ namespace { using testing::Contains; +using Sqlite::ColumnType; +using Sqlite::JournalMode; +using Sqlite::OpenMode; using Sqlite::SqliteTable; class SqliteDatabase : public ::testing::Test diff --git a/tests/unit/unittest/sqlitedatabasebackend-test.cpp b/tests/unit/unittest/sqlitedatabasebackend-test.cpp index 90252eb39fa32ebb40640d83a243c86b64bb277e..67648a60c5cfbb51a7fa0198c6e451adf09351d6 100644 --- a/tests/unit/unittest/sqlitedatabasebackend-test.cpp +++ b/tests/unit/unittest/sqlitedatabasebackend-test.cpp @@ -38,6 +38,11 @@ namespace { using Backend = Sqlite::SqliteDatabaseBackend; +using Sqlite::ColumnType; +using Sqlite::Contraint; +using Sqlite::JournalMode; +using Sqlite::OpenMode; +using Sqlite::TextEncoding; using Sqlite::SqliteException; using Sqlite::SqliteWriteStatement; @@ -106,35 +111,35 @@ TEST_F(SqliteDatabaseBackend, PersistJournalMode) TEST_F(SqliteDatabaseBackend, DefaultTextEncoding) { - ASSERT_THAT(databaseBackend.textEncoding(), Utf8); + ASSERT_THAT(databaseBackend.textEncoding(), TextEncoding::Utf8); } TEST_F(SqliteDatabaseBackend, Utf16TextEncoding) { - databaseBackend.setTextEncoding(Utf16); + databaseBackend.setTextEncoding(TextEncoding::Utf16); - ASSERT_THAT(databaseBackend.textEncoding(), Utf16); + ASSERT_THAT(databaseBackend.textEncoding(), TextEncoding::Utf16); } TEST_F(SqliteDatabaseBackend, Utf16beTextEncoding) { - databaseBackend.setTextEncoding(Utf16be); + databaseBackend.setTextEncoding(TextEncoding::Utf16be); - ASSERT_THAT(databaseBackend.textEncoding(), Utf16be); + ASSERT_THAT(databaseBackend.textEncoding(),TextEncoding::Utf16be); } TEST_F(SqliteDatabaseBackend, Utf16leTextEncoding) { - databaseBackend.setTextEncoding(Utf16le); + databaseBackend.setTextEncoding(TextEncoding::Utf16le); - ASSERT_THAT(databaseBackend.textEncoding(), Utf16le); + ASSERT_THAT(databaseBackend.textEncoding(), TextEncoding::Utf16le); } TEST_F(SqliteDatabaseBackend, Utf8TextEncoding) { - databaseBackend.setTextEncoding(Utf8); + databaseBackend.setTextEncoding(TextEncoding::Utf8); - ASSERT_THAT(databaseBackend.textEncoding(), Utf8); + ASSERT_THAT(databaseBackend.textEncoding(), TextEncoding::Utf8); } TEST_F(SqliteDatabaseBackend, TextEncodingCannotBeChangedAfterTouchingDatabase) @@ -143,7 +148,7 @@ TEST_F(SqliteDatabaseBackend, TextEncodingCannotBeChangedAfterTouchingDatabase) databaseBackend.execute("CREATE TABLE text(name, number)"); - ASSERT_THROW(databaseBackend.setTextEncoding(Utf16), SqliteException); + ASSERT_THROW(databaseBackend.setTextEncoding(TextEncoding::Utf16), SqliteException); } TEST_F(SqliteDatabaseBackend, OpenModeReadOnly) diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp index 3bf97e6d73d5a3235898b5e6f7d4cba7268a7652..b3523bdc77056130b5fda29a20d0cba721f25913 100644 --- a/tests/unit/unittest/sqlitestatement-test.cpp +++ b/tests/unit/unittest/sqlitestatement-test.cpp @@ -30,14 +30,15 @@ #include <sqlitereadwritestatement.h> #include <sqlitewritestatement.h> +#include <utils/smallstringio.h> + #include <QDir> #include <vector> namespace { -using testing::ElementsAre; -using testing::PrintToString; +using Sqlite::JournalMode; using Sqlite::SqliteException; using Sqlite::SqliteDatabase; using Sqlite::SqliteReadStatement; @@ -71,6 +72,21 @@ protected: SqliteDatabase database; }; +struct Output +{ + Utils::SmallString name; + Utils::SmallString number; + long long value; + friend bool operator==(const Output &f, const Output &s) + { + return f.name == s.name && f.number == s.number && f.value == s.value; + } + friend std::ostream &operator<<(std::ostream &out, const Output &o) + { + return out << "(" << o.name << ", " << ", " << o.number<< ", " << o.value<< ")"; + } +}; + TEST_F(SqliteStatement, PrepareFailure) { ASSERT_THROW(SqliteReadStatement("blah blah blah", database), SqliteException); @@ -99,11 +115,11 @@ TEST_F(SqliteStatement, Value) statement.next(); ASSERT_THAT(statement.value<int>(0), 0); - ASSERT_THAT(statement.value<qint64>(0), 0); + ASSERT_THAT(statement.value<int64_t>(0), 0); ASSERT_THAT(statement.value<double>(0), 0.0); ASSERT_THAT(statement.text(0), "foo"); ASSERT_THAT(statement.value<int>(1), 23); - ASSERT_THAT(statement.value<qint64>(1), 23); + ASSERT_THAT(statement.value<int64_t>(1), 23); ASSERT_THAT(statement.value<double>(1), 23.3); ASSERT_THAT(statement.text(1), "23.3"); } @@ -127,12 +143,14 @@ TEST_F(SqliteStatement, ValueFailure) TEST_F(SqliteStatement, ToIntergerValue) { - ASSERT_THAT(SqliteReadStatement::toValue<int>("SELECT number FROM test WHERE name='foo'", database), 23); + auto value = SqliteReadStatement::toValue<int>("SELECT number FROM test WHERE name='foo'", database); + + ASSERT_THAT(value, 23); } TEST_F(SqliteStatement, ToLongIntergerValue) { - ASSERT_THAT(SqliteReadStatement::toValue<qint64>("SELECT number FROM test WHERE name='foo'", database), 23LL); + ASSERT_THAT(SqliteReadStatement::toValue<qint64>("SELECT number FROM test WHERE name='foo'", database), Eq(23)); } TEST_F(SqliteStatement, ToDoubleValue) @@ -145,31 +163,6 @@ TEST_F(SqliteStatement, ToStringValue) ASSERT_THAT(SqliteReadStatement::toValue<Utils::SmallString>("SELECT name FROM test WHERE name='foo'", database), "foo"); } -TEST_F(SqliteStatement, Utf8Values) -{ - SqliteReadStatement statement("SELECT name, number FROM test ORDER by name", database); - - auto values = statement.values<Utils::SmallStringVector>(); - - ASSERT_THAT(values, ElementsAre("bar", "foo", "poo")); -} -TEST_F(SqliteStatement, DoubleValues) -{ - SqliteReadStatement statement("SELECT name, number FROM test ORDER by name", database); - - auto values = statement.values<std::vector<double>>(1); - - ASSERT_THAT(values, ElementsAre(0.0, 23.3, 40.0)); -} - -TEST_F(SqliteStatement, ValuesFailure) -{ - SqliteReadStatement statement("SELECT name, number FROM test", database); - - ASSERT_THROW(statement.values<Utils::SmallStringVector>({1, 2}), SqliteException); - ASSERT_THROW(statement.values<Utils::SmallStringVector>({-1, 1}), SqliteException); -} - TEST_F(SqliteStatement, ColumnNames) { SqliteReadStatement statement("SELECT name, number FROM test", database); @@ -206,7 +199,7 @@ TEST_F(SqliteStatement, BindLongInteger) { SqliteReadStatement statement("SELECT name, number FROM test WHERE number=?", database); - statement.bind(1, qint64(40)); + statement.bind(1, int64_t(40)); statement.next(); ASSERT_THAT(statement.text(0), "poo"); @@ -236,7 +229,7 @@ TEST_F(SqliteStatement, BindLongIntegerByParameter) { SqliteReadStatement statement("SELECT name, number FROM test WHERE number=@number", database); - statement.bind("@number", qint64(40)); + statement.bind("@number", int64_t(40)); statement.next(); ASSERT_THAT(statement.text(0), "poo"); @@ -315,14 +308,156 @@ TEST_F(SqliteStatement, ClosedDatabase) database.open(QDir::tempPath() + QStringLiteral("/SqliteStatementTest.db")); } +TEST_F(SqliteStatement, GetTupleValuesWithoutArguments) +{ + using Tuple = std::tuple<Utils::SmallString, double, int>; + SqliteReadStatement statement("SELECT name, number, value FROM test", database); + + auto values = statement.tupleValues<Utils::SmallString, double, int>(3); + + ASSERT_THAT(values, ElementsAre(Tuple{"bar", 0, 1}, + Tuple{"foo", 23.3, 2}, + Tuple{"poo", 40.0, 3})); +} + +TEST_F(SqliteStatement, GetSingleValuesWithoutArguments) +{ + SqliteReadStatement statement("SELECT name FROM test", database); + + std::vector<Utils::SmallString> values = statement.values<Utils::SmallString>(3); + + ASSERT_THAT(values, ElementsAre("bar", "foo", "poo")); +} + +TEST_F(SqliteStatement, GetStructValuesWithoutArguments) +{ + SqliteReadStatement statement("SELECT name, number, value FROM test", database); + + auto values = statement.structValues<Output, Utils::SmallString, Utils::SmallString, long long>(3); + + ASSERT_THAT(values, ElementsAre(Output{"bar", "blah", 1}, + Output{"foo", "23.3", 2}, + Output{"poo", "40", 3})); +} + + +TEST_F(SqliteStatement, GetValuesForSingleOutputWithBindingMultipleTimes) +{ + SqliteReadStatement statement("SELECT name FROM test WHERE number=?", database); + statement.values<Utils::SmallString>(3, 40); + + std::vector<Utils::SmallString> values = statement.values<Utils::SmallString>(3, 40); + + ASSERT_THAT(values, ElementsAre("poo")); +} + +TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndContainerQueryValues) +{ + using Tuple = std::tuple<Utils::SmallString, double, double>; + std::vector<double> queryValues = {40, 23.3}; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE number=?", database); + + auto values = statement.tupleValues<Utils::SmallString, double, double>(3, queryValues); + + ASSERT_THAT(values, ElementsAre(Tuple{"poo", 40, 3.}, + Tuple{"foo", 23.3, 2.})); +} + +TEST_F(SqliteStatement, GetValuesForSingleOutputValuesAndContainerQueryValues) +{ + std::vector<double> queryValues = {40, 23.3}; + SqliteReadStatement statement("SELECT name, number FROM test WHERE number=?", database); + + std::vector<Utils::SmallString> values = statement.values<Utils::SmallString>(3, queryValues); + + ASSERT_THAT(values, ElementsAre("poo", "foo")); +} + +TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndContainerQueryTupleValues) +{ + using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, int>; + using Tuple2 = std::tuple<Utils::SmallString, double, int>; + std::vector<Tuple> queryValues = {{"poo", "40", 3}, {"bar", "blah", 1}}; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE name= ? AND number=? AND value=?", database); + + auto values = statement.tupleValues<Utils::SmallString, double, int>(3, queryValues); + + ASSERT_THAT(values, ElementsAre(Tuple2{"poo", 40, 3}, + Tuple2{"bar", 0, 1})); +} + +TEST_F(SqliteStatement, GetValuesForSingleOutputValuesAndContainerQueryTupleValues) +{ + using Tuple = std::tuple<Utils::SmallString, Utils::SmallString>; + std::vector<Tuple> queryValues = {{"poo", "40"}, {"bar", "blah"}}; + SqliteReadStatement statement("SELECT name, number FROM test WHERE name= ? AND number=?", database); + + std::vector<Utils::SmallString> values = statement.values<Utils::SmallString>(3, queryValues); + + ASSERT_THAT(values, ElementsAre("poo", "bar")); +} + +TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndMultipleQueryValue) +{ + using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); + + auto values = statement.tupleValues<Utils::SmallString, Utils::SmallString, long long>(3, "bar", "blah", 1); + + ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); +} + +TEST_F(SqliteStatement, CallGetValuesForMultipleOutputValuesAndMultipleQueryValueMultipleTimes) +{ + using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE name=? AND number=?", database); + statement.tupleValues<Utils::SmallString, Utils::SmallString, long long>(3, "bar", "blah"); + + auto values = statement.tupleValues<Utils::SmallString, Utils::SmallString, long long>(3, "bar", "blah"); + + ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); +} + +TEST_F(SqliteStatement, GetStructOutputValuesAndMultipleQueryValue) +{ + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); + + auto values = statement.structValues<Output, Utils::SmallString, Utils::SmallString, long long>(3, "bar", "blah", 1); + + ASSERT_THAT(values, ElementsAre(Output{"bar", "blah", 1})); +} + +TEST_F(SqliteStatement, GetStructOutputValuesAndContainerQueryValues) +{ + std::vector<double> queryValues = {40, 23.3}; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE number=?", database); + + auto values = statement.structValues<Output, Utils::SmallString, Utils::SmallString, long long>(3, queryValues); + + ASSERT_THAT(values, ElementsAre(Output{"poo", "40", 3}, + Output{"foo", "23.3", 2})); +} + +TEST_F(SqliteStatement, GetStructOutputValuesAndContainerQueryTupleValues) +{ + using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, int>; + std::vector<Tuple> queryValues = {{"poo", "40", 3}, {"bar", "blah", 1}}; + SqliteReadStatement statement("SELECT name, number, value FROM test WHERE name= ? AND number=? AND value=?", database); + + auto values = statement.structValues<Output, Utils::SmallString, Utils::SmallString, long long>(3, queryValues); + + ASSERT_THAT(values, ElementsAre(Output{"poo", "40", 3}, + Output{"bar", "blah", 1})); +} + void SqliteStatement::SetUp() { database.setJournalMode(JournalMode::Memory); database.open(":memory:"); - database.execute("CREATE TABLE test(name TEXT UNIQUE, number NUMERIC)"); - database.execute("INSERT INTO test VALUES ('bar', 'blah')"); - database.execute("INSERT INTO test VALUES ('foo', 23.3)"); - database.execute("INSERT INTO test VALUES ('poo', 40)"); + database.execute("CREATE TABLE test(name TEXT UNIQUE, number NUMERIC, value NUMERIC)"); + database.execute("INSERT INTO test VALUES ('bar', 'blah', 1)"); + database.execute("INSERT INTO test VALUES ('foo', 23.3, 2)"); + database.execute("INSERT INTO test VALUES ('poo', 40, 3)"); } void SqliteStatement::TearDown() diff --git a/tests/unit/unittest/sqlitetable-test.cpp b/tests/unit/unittest/sqlitetable-test.cpp index 9c1b2aa79ab4f4bb275c11d0cf18ac377667e918..7873181580c7fedc18fffcb3841886edbed4cc5f 100644 --- a/tests/unit/unittest/sqlitetable-test.cpp +++ b/tests/unit/unittest/sqlitetable-test.cpp @@ -32,6 +32,9 @@ namespace { +using Sqlite::ColumnType; +using Sqlite::JournalMode; +using Sqlite::OpenMode; using Sqlite::SqliteColumn; using Sqlite::SqliteDatabase; diff --git a/tests/unit/unittest/sqlstatementbuilder-test.cpp b/tests/unit/unittest/sqlstatementbuilder-test.cpp index 028a141c72947b7ed0c7c6622071cd5bcdbf08b5..ef3a66e1da5b7f1873cf9ee054bbb53b36f26d65 100644 --- a/tests/unit/unittest/sqlstatementbuilder-test.cpp +++ b/tests/unit/unittest/sqlstatementbuilder-test.cpp @@ -30,6 +30,7 @@ using namespace ::testing; +using Sqlite::ColumnType; using Sqlite::SqlStatementBuilder; using Sqlite::SqlStatementBuilderException;