Commit 1a25b615 authored by Marco Bubke's avatar Marco Bubke

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's avatarTim Jenssen <tim.jenssen@qt.io>
parent 8617f497
......@@ -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
......@@ -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
......@@ -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>;
......
......@@ -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()
......
......@@ -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;
......
......@@ -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)
......
......@@ -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
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
This diff is collapsed.
......@@ -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
......@@ -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;
};
......