Sqlite: Improve Sqlite wrapper

It is now possible to read values at once.

for (auto [name, value] : statement.tupleValues<String, int>(1000, "foo", 20))
   ....

Change-Id: I3d4bc5218810b4620e1df625126aa490f30bbc71
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2017-08-17 15:33:25 +02:00
parent 8617f497bc
commit 1a25b61576
24 changed files with 769 additions and 277 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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>;

View File

@@ -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()

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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

View File

@@ -32,43 +32,66 @@ namespace Sqlite {
class SqliteDatabaseBackend;
class SqliteDatabase;
class SQLITE_EXPORT SqliteAbstractTransaction
template <typename Database>
class SqliteAbstractTransaction
{
public:
virtual ~SqliteAbstractTransaction();
virtual ~SqliteAbstractTransaction()
{
if (!m_isAlreadyCommited)
m_database.execute("ROLLBACK");
}
void commit();
void commit()
{
m_database.execute("COMMIT");
m_isAlreadyCommited = true;
}
protected:
SqliteAbstractTransaction(SqliteDatabaseBackend &backend);
SqliteAbstractTransaction(SqliteDatabase &database);
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");
}
};

View File

@@ -33,7 +33,7 @@
namespace Sqlite {
SqlStatementBuilder::SqlStatementBuilder(Utils::SmallString &&sqlTemplate)
SqlStatementBuilder::SqlStatementBuilder(Utils::SmallStringView sqlTemplate)
: m_sqlTemplate(std::move(sqlTemplate))
{
}

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -32,6 +32,9 @@
namespace {
using Sqlite::ColumnType;
using Sqlite::JournalMode;
using Sqlite::OpenMode;
using Sqlite::SqliteColumn;
using Sqlite::SqliteDatabase;

View File

@@ -30,6 +30,7 @@
using namespace ::testing;
using Sqlite::ColumnType;
using Sqlite::SqlStatementBuilder;
using Sqlite::SqlStatementBuilderException;