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 @@ ...@@ -28,12 +28,11 @@
namespace Sqlite { namespace Sqlite {
CreateTableSqlStatementBuilder::CreateTableSqlStatementBuilder() CreateTableSqlStatementBuilder::CreateTableSqlStatementBuilder()
: m_sqlStatementBuilder("CREATE TABLE IF NOT EXISTS $table($columnDefinitions)$withoutRowId"), : m_sqlStatementBuilder("CREATE $temporaryTABLE $ifNotExits$table($columnDefinitions)$withoutRowId")
m_useWithoutRowId(false)
{ {
} }
void CreateTableSqlStatementBuilder::setTable(Utils::SmallString &&tableName) void CreateTableSqlStatementBuilder::setTableName(Utils::SmallString &&tableName)
{ {
m_sqlStatementBuilder.clear(); m_sqlStatementBuilder.clear();
...@@ -42,11 +41,11 @@ void CreateTableSqlStatementBuilder::setTable(Utils::SmallString &&tableName) ...@@ -42,11 +41,11 @@ void CreateTableSqlStatementBuilder::setTable(Utils::SmallString &&tableName)
void CreateTableSqlStatementBuilder::addColumn(Utils::SmallString &&columnName, void CreateTableSqlStatementBuilder::addColumn(Utils::SmallString &&columnName,
ColumnType columnType, ColumnType columnType,
IsPrimaryKey isPrimaryKey) Contraint constraint)
{ {
m_sqlStatementBuilder.clear(); 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) void CreateTableSqlStatementBuilder::setColumns(const SqliteColumns &columns)
...@@ -61,6 +60,16 @@ void CreateTableSqlStatementBuilder::setUseWithoutRowId(bool useWithoutRowId) ...@@ -61,6 +60,16 @@ void CreateTableSqlStatementBuilder::setUseWithoutRowId(bool useWithoutRowId)
m_useWithoutRowId = useWithoutRowId; m_useWithoutRowId = useWithoutRowId;
} }
void CreateTableSqlStatementBuilder::setUseIfNotExists(bool useIfNotExists)
{
m_useIfNotExits = useIfNotExists;
}
void CreateTableSqlStatementBuilder::setUseTemporaryTable(bool useTemporaryTable)
{
m_useTemporaryTable = useTemporaryTable;
}
void CreateTableSqlStatementBuilder::clear() void CreateTableSqlStatementBuilder::clear()
{ {
m_sqlStatementBuilder.clear(); m_sqlStatementBuilder.clear();
...@@ -95,8 +104,11 @@ void CreateTableSqlStatementBuilder::bindColumnDefinitions() const ...@@ -95,8 +104,11 @@ void CreateTableSqlStatementBuilder::bindColumnDefinitions() const
for (const SqliteColumn &columns : m_columns) { for (const SqliteColumn &columns : m_columns) {
Utils::SmallString columnDefinitionString = {columns.name(), " ", columns.typeString()}; Utils::SmallString columnDefinitionString = {columns.name(), " ", columns.typeString()};
if (columns.isPrimaryKey()) switch (columns.constraint()) {
columnDefinitionString.append(" PRIMARY KEY"); case Contraint::PrimaryKey: columnDefinitionString.append(" PRIMARY KEY"); break;
case Contraint::Unique: columnDefinitionString.append(" UNIQUE"); break;
case Contraint::NoConstraint: break;
}
columnDefinitionStrings.push_back(columnDefinitionString); columnDefinitionStrings.push_back(columnDefinitionString);
} }
...@@ -108,12 +120,34 @@ void CreateTableSqlStatementBuilder::bindAll() const ...@@ -108,12 +120,34 @@ void CreateTableSqlStatementBuilder::bindAll() const
{ {
m_sqlStatementBuilder.bind("$table", m_tableName.clone()); m_sqlStatementBuilder.bind("$table", m_tableName.clone());
bindTemporary();
bindIfNotExists();
bindColumnDefinitions(); bindColumnDefinitions();
bindWithoutRowId();
}
void CreateTableSqlStatementBuilder::bindWithoutRowId() const
{
if (m_useWithoutRowId) if (m_useWithoutRowId)
m_sqlStatementBuilder.bind("$withoutRowId", " WITHOUT ROWID"); m_sqlStatementBuilder.bind("$withoutRowId", " WITHOUT ROWID");
else else
m_sqlStatementBuilder.bindEmptyText("$withoutRowId"); 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 } // namespace Sqlite
...@@ -35,12 +35,14 @@ class SQLITE_EXPORT CreateTableSqlStatementBuilder ...@@ -35,12 +35,14 @@ class SQLITE_EXPORT CreateTableSqlStatementBuilder
public: public:
CreateTableSqlStatementBuilder(); CreateTableSqlStatementBuilder();
void setTable(Utils::SmallString &&tableName); void setTableName(Utils::SmallString &&tableName);
void addColumn(Utils::SmallString &&columnName, void addColumn(Utils::SmallString &&columnName,
ColumnType columnType, ColumnType columnType,
IsPrimaryKey isPrimaryKey = IsPrimaryKey::No); Contraint constraint = Contraint::NoConstraint);
void setColumns(const SqliteColumns &columns); void setColumns(const SqliteColumns &columns);
void setUseWithoutRowId(bool useWithoutRowId); void setUseWithoutRowId(bool useWithoutRowId);
void setUseIfNotExists(bool useIfNotExists);
void setUseTemporaryTable(bool useTemporaryTable);
void clear(); void clear();
void clearColumns(); void clearColumns();
...@@ -52,12 +54,17 @@ public: ...@@ -52,12 +54,17 @@ public:
protected: protected:
void bindColumnDefinitions() const; void bindColumnDefinitions() const;
void bindAll() const; void bindAll() const;
void bindWithoutRowId() const;
void bindIfNotExists() const;
void bindTemporary() const;
private: private:
mutable SqlStatementBuilder m_sqlStatementBuilder; mutable SqlStatementBuilder m_sqlStatementBuilder;
Utils::SmallString m_tableName; Utils::SmallString m_tableName;
SqliteColumns m_columns; SqliteColumns m_columns;
bool m_useWithoutRowId; bool m_useWithoutRowId = false;
bool m_useIfNotExits = false;
bool m_useTemporaryTable = false;
}; };
} // namespace Sqlite } // namespace Sqlite
...@@ -38,17 +38,17 @@ public: ...@@ -38,17 +38,17 @@ public:
SqliteColumn(Utils::SmallString &&name, SqliteColumn(Utils::SmallString &&name,
ColumnType type = ColumnType::Numeric, ColumnType type = ColumnType::Numeric,
IsPrimaryKey isPrimaryKey = IsPrimaryKey::No) Contraint constraint = Contraint::NoConstraint)
: m_name(std::move(name)), : m_name(std::move(name)),
m_type(type), m_type(type),
m_isPrimaryKey(isPrimaryKey) m_constraint(constraint)
{} {}
void clear() void clear()
{ {
m_name.clear(); m_name.clear();
m_type = ColumnType::Numeric; m_type = ColumnType::Numeric;
m_isPrimaryKey = IsPrimaryKey::No; m_constraint = Contraint::NoConstraint;
} }
void setName(Utils::SmallString &&newName) void setName(Utils::SmallString &&newName)
...@@ -71,14 +71,14 @@ public: ...@@ -71,14 +71,14 @@ public:
return m_type; 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 Utils::SmallString typeString() const
...@@ -98,13 +98,13 @@ public: ...@@ -98,13 +98,13 @@ public:
{ {
return first.m_name == second.m_name return first.m_name == second.m_name
&& first.m_type == second.m_type && first.m_type == second.m_type
&& first.m_isPrimaryKey == second.m_isPrimaryKey; && first.m_constraint == second.m_constraint;
} }
private: private:
Utils::SmallString m_name; Utils::SmallString m_name;
ColumnType m_type = ColumnType::Numeric; ColumnType m_type = ColumnType::Numeric;
IsPrimaryKey m_isPrimaryKey = IsPrimaryKey::No; Contraint m_constraint = Contraint::NoConstraint;
}; };
using SqliteColumns = std::vector<SqliteColumn>; using SqliteColumns = std::vector<SqliteColumn>;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "sqlitedatabase.h" #include "sqlitedatabase.h"
#include "sqlitetable.h" #include "sqlitetable.h"
#include "sqlitetransaction.h"
namespace Sqlite { namespace Sqlite {
...@@ -34,6 +35,12 @@ SqliteDatabase::SqliteDatabase() ...@@ -34,6 +35,12 @@ SqliteDatabase::SqliteDatabase()
{ {
} }
SqliteDatabase::SqliteDatabase(Utils::PathString &&databaseFilePath)
: m_databaseBackend(*this)
{
open(std::move(databaseFilePath));
}
void SqliteDatabase::open() void SqliteDatabase::open()
{ {
m_databaseBackend.open(m_databaseFilePath, m_openMode); m_databaseBackend.open(m_databaseFilePath, m_openMode);
...@@ -61,7 +68,7 @@ bool SqliteDatabase::isOpen() const ...@@ -61,7 +68,7 @@ bool SqliteDatabase::isOpen() const
SqliteTable &SqliteDatabase::addTable() SqliteTable &SqliteDatabase::addTable()
{ {
m_sqliteTables.emplace_back(*this); m_sqliteTables.emplace_back();
return m_sqliteTables.back(); return m_sqliteTables.back();
} }
...@@ -118,8 +125,12 @@ void SqliteDatabase::execute(Utils::SmallStringView sqlStatement) ...@@ -118,8 +125,12 @@ void SqliteDatabase::execute(Utils::SmallStringView sqlStatement)
void SqliteDatabase::initializeTables() void SqliteDatabase::initializeTables()
{ {
SqliteImmediateTransaction<SqliteDatabase> transaction(*this);
for (SqliteTable &table : m_sqliteTables) for (SqliteTable &table : m_sqliteTables)
table.initialize(); table.initialize(*this);
transaction.commit();
} }
SqliteDatabaseBackend &SqliteDatabase::backend() SqliteDatabaseBackend &SqliteDatabase::backend()
......
...@@ -37,12 +37,14 @@ namespace Sqlite { ...@@ -37,12 +37,14 @@ namespace Sqlite {
class SQLITE_EXPORT SqliteDatabase class SQLITE_EXPORT SqliteDatabase
{ {
template <typename Database>
friend class SqliteAbstractTransaction; friend class SqliteAbstractTransaction;
friend class SqliteStatement; friend class SqliteStatement;
friend class SqliteBackend; friend class SqliteBackend;
public: public:
SqliteDatabase(); SqliteDatabase();
SqliteDatabase(Utils::PathString &&databaseFilePath);
SqliteDatabase(const SqliteDatabase &) = delete; SqliteDatabase(const SqliteDatabase &) = delete;
bool operator=(const SqliteDatabase &) = delete; bool operator=(const SqliteDatabase &) = delete;
......
...@@ -38,14 +38,6 @@ ...@@ -38,14 +38,6 @@
#include "sqlite3.h" #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 { namespace Sqlite {
SqliteDatabaseBackend::SqliteDatabaseBackend(SqliteDatabase &database) SqliteDatabaseBackend::SqliteDatabaseBackend(SqliteDatabase &database)
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
# define SQLITE_EXPORT Q_DECL_IMPORT # define SQLITE_EXPORT Q_DECL_IMPORT
#endif #endif
namespace Sqlite {
enum class ColumnType : char enum class ColumnType : char
{ {
Numeric, Numeric,
...@@ -46,10 +48,11 @@ enum class ColumnType : char ...@@ -46,10 +48,11 @@ enum class ColumnType : char
None None
}; };
enum class IsPrimaryKey : char enum class Contraint : char
{ {
No, NoConstraint,
Yes PrimaryKey,
Unique
}; };
enum class ColumnConstraint : char enum class ColumnConstraint : char
...@@ -84,3 +87,5 @@ enum TextEncoding : char ...@@ -84,3 +87,5 @@ enum TextEncoding : char
#endif #endif
}; };
} // namespace Sqlite
...@@ -37,6 +37,8 @@ public: ...@@ -37,6 +37,8 @@ public:
using SqliteStatement::next; using SqliteStatement::next;
using SqliteStatement::reset; using SqliteStatement::reset;
using SqliteStatement::value; using SqliteStatement::value;
using SqliteStatement::structValues;
using SqliteStatement::tupleValues;
using SqliteStatement::text; using SqliteStatement::text;
using SqliteStatement::values; using SqliteStatement::values;
using SqliteStatement::columnCount; using SqliteStatement::columnCount;
......
...@@ -48,6 +48,8 @@ public: ...@@ -48,6 +48,8 @@ public:
using SqliteStatement::value; using SqliteStatement::value;
using SqliteStatement::text; using SqliteStatement::text;
using SqliteStatement::values; using SqliteStatement::values;
using SqliteStatement::structValues;
using SqliteStatement::tupleValues;
using SqliteStatement::columnCount; using SqliteStatement::columnCount;
using SqliteStatement::columnNames; using SqliteStatement::columnNames;
using SqliteStatement::toValue; using SqliteStatement::toValue;
......
...@@ -29,13 +29,11 @@ ...@@ -29,13 +29,11 @@
#include "sqlitedatabasebackend.h" #include "sqlitedatabasebackend.h"
#include "sqliteexception.h" #include "sqliteexception.h"
#include <QMutex>
#include <QtGlobal>
#include <QVariant>
#include <QWaitCondition>
#include "sqlite3.h" #include "sqlite3.h"
#include <condition_variable>
#include <mutex>
#if defined(__GNUC__) #if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wignored-qualifiers" # pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif #endif
...@@ -61,8 +59,8 @@ void SqliteStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement) ...@@ -61,8 +59,8 @@ void SqliteStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement)
sqlite3_finalize(compiledStatement); sqlite3_finalize(compiledStatement);
} }
class UnlockNotification { class UnlockNotification
{
public: public:
static void unlockNotifyCallBack(void **arguments, int argumentCount) static void unlockNotifyCallBack(void **arguments, int argumentCount)
{ {
...@@ -74,27 +72,24 @@ public: ...@@ -74,27 +72,24 @@ public:
void wakeupWaitCondition() void wakeupWaitCondition()
{ {
mutex.lock(); {
fired = 1; std::lock_guard<std::mutex> lock(m_mutex);
waitCondition.wakeAll(); m_fired = 1;
mutex.unlock(); }
m_waitCondition.notify_all();
} }
void wait() 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: private:
bool fired = false; bool m_fired = false;
QWaitCondition waitCondition; std::condition_variable m_waitCondition;
QMutex mutex; std::mutex m_mutex;
}; };
void SqliteStatement::waitForUnlockNotify() const void SqliteStatement::waitForUnlockNotify() const
...@@ -143,6 +138,7 @@ void SqliteStatement::step() const ...@@ -143,6 +138,7 @@ void SqliteStatement::step() const
void SqliteStatement::execute() const void SqliteStatement::execute() const
{ {
next(); next();
reset();
} }
int SqliteStatement::columnCount() const int SqliteStatement::columnCount() const
...@@ -168,7 +164,7 @@ void SqliteStatement::bind(int index, int value) ...@@ -168,7 +164,7 @@ void SqliteStatement::bind(int index, int value)
throwException("SqliteStatement::bind: cant' bind 32 bit integer!"); 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); int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK) if (resultCode != SQLITE_OK)
...@@ -198,7 +194,8 @@ void SqliteStatement::bind(Utils::SmallStringView name, Type value) ...@@ -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, 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, double value);
template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text); template SQLITE_EXPORT void SqliteStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text);
...@@ -363,21 +360,23 @@ SqliteDatabase &SqliteStatement::database() const ...@@ -363,21 +360,23 @@ SqliteDatabase &SqliteStatement::database() const
return m_database; 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)); const char *text = reinterpret_cast<const char*>(sqlite3_column_text(sqlStatment, column));
std::size_t size = std::size_t(sqlite3_column_bytes(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); int dataType = sqlite3_column_type(sqlStatment, column);
switch (dataType) { switch (dataType) {
case SQLITE_INTEGER: case SQLITE_INTEGER:
case SQLITE_FLOAT: case SQLITE_FLOAT:
case SQLITE3_TEXT: return textForColumn(sqlStatment, column); case SQLITE3_TEXT: return textForColumn<StringType>(sqlStatment, column);
case SQLITE_BLOB: case SQLITE_BLOB:
case SQLITE_NULL: return {}; case SQLITE_NULL: return {};
}