mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
445 lines
9.6 KiB
C++
445 lines
9.6 KiB
C++
#include <db/DBManager.h>
|
|
|
|
#include <QSqlDatabase>
|
|
#include <QSqlError>
|
|
#include <QSqlQuery>
|
|
#include <QSqlRecord>
|
|
#include <QThreadStorage>
|
|
#include <QUuid>
|
|
#include <QDir>
|
|
|
|
#ifdef _WIN32
|
|
#include <stdexcept>
|
|
#endif
|
|
|
|
// not in header because of linking
|
|
static QString _rootPath;
|
|
static QThreadStorage<QSqlDatabase> _databasePool;
|
|
|
|
DBManager::DBManager(QObject* parent)
|
|
: QObject(parent)
|
|
, _log(Logger::getInstance("DB"))
|
|
, _readonlyMode (false)
|
|
{
|
|
}
|
|
|
|
DBManager::~DBManager()
|
|
{
|
|
}
|
|
|
|
void DBManager::setRootPath(const QString& rootPath)
|
|
{
|
|
_rootPath = rootPath;
|
|
// create directory
|
|
QDir().mkpath(_rootPath+"/db");
|
|
}
|
|
|
|
void DBManager::setTable(const QString& table)
|
|
{
|
|
_table = table;
|
|
}
|
|
|
|
QSqlDatabase DBManager::getDB() const
|
|
{
|
|
if(_databasePool.hasLocalData())
|
|
return _databasePool.localData();
|
|
else
|
|
{
|
|
auto db = QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString());
|
|
_databasePool.setLocalData(db);
|
|
db.setDatabaseName(_rootPath+"/db/"+_dbn+".db");
|
|
if(!db.open())
|
|
{
|
|
Error(_log, QSTRING_CSTR(db.lastError().text()));
|
|
throw std::runtime_error("Failed to open database connection!");
|
|
}
|
|
return db;
|
|
}
|
|
}
|
|
|
|
bool DBManager::createRecord(const VectorPair& conditions, const QVariantMap& columns) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(recordExists(conditions))
|
|
{
|
|
// if there is no column data, return
|
|
if(columns.isEmpty())
|
|
return true;
|
|
|
|
if(!updateRecord(conditions, columns))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
query.setForwardOnly(true);
|
|
|
|
QVariantList cValues;
|
|
QStringList prep;
|
|
QStringList placeh;
|
|
// prep merge columns & condition
|
|
QVariantMap::const_iterator i = columns.constBegin();
|
|
while (i != columns.constEnd()) {
|
|
prep.append(i.key());
|
|
cValues += i.value();
|
|
placeh.append("?");
|
|
|
|
++i;
|
|
}
|
|
for(const auto& pair : conditions)
|
|
{
|
|
// remove the condition statements
|
|
QString tmp = pair.first;
|
|
prep << tmp.remove("AND");
|
|
cValues << pair.second;
|
|
placeh.append("?");
|
|
}
|
|
query.prepare(QString("INSERT INTO %1 ( %2 ) VALUES ( %3 )").arg(_table,prep.join(", ")).arg(placeh.join(", ")));
|
|
// add column & condition values
|
|
doAddBindValue(query, cValues);
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed to create record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prep.join(", ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBManager::recordExists(const VectorPair& conditions) const
|
|
{
|
|
if(conditions.isEmpty())
|
|
return false;
|
|
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
query.setForwardOnly(true);
|
|
|
|
QStringList prepCond;
|
|
QVariantList bindVal;
|
|
prepCond << "WHERE";
|
|
|
|
for(const auto& pair : conditions)
|
|
{
|
|
prepCond << pair.first+"=?";
|
|
bindVal << pair.second;
|
|
}
|
|
query.prepare(QString("SELECT * FROM %1 %2").arg(_table,prepCond.join(" ")));
|
|
doAddBindValue(query, bindVal);
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed recordExists(): '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
|
|
int entry = 0;
|
|
while (query.next()) {
|
|
entry++;
|
|
}
|
|
|
|
if(entry)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DBManager::updateRecord(const VectorPair& conditions, const QVariantMap& columns) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
query.setForwardOnly(true);
|
|
|
|
QVariantList values;
|
|
QStringList prep;
|
|
|
|
// prepare columns valus
|
|
QVariantMap::const_iterator i = columns.constBegin();
|
|
while (i != columns.constEnd()) {
|
|
prep += i.key()+"=?";
|
|
values += i.value();
|
|
|
|
++i;
|
|
}
|
|
|
|
// prepare condition values
|
|
QStringList prepCond;
|
|
QVariantList prepBindVal;
|
|
if(!conditions.isEmpty())
|
|
prepCond << "WHERE";
|
|
|
|
for(const auto& pair : conditions)
|
|
{
|
|
prepCond << pair.first+"=?";
|
|
prepBindVal << pair.second;
|
|
}
|
|
|
|
query.prepare(QString("UPDATE %1 SET %2 %3").arg(_table,prep.join(", ")).arg(prepCond.join(" ")));
|
|
// add column values
|
|
doAddBindValue(query, values);
|
|
// add condition values
|
|
doAddBindValue(query, prepBindVal);
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed to update record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBManager::getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns, const QStringList& tOrder) const
|
|
{
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
query.setForwardOnly(true);
|
|
|
|
QString sColumns("*");
|
|
if(!tColumns.isEmpty())
|
|
sColumns = tColumns.join(", ");
|
|
|
|
QString sOrder("");
|
|
if(!tOrder.isEmpty())
|
|
{
|
|
sOrder = " ORDER BY ";
|
|
sOrder.append(tOrder.join(", "));
|
|
}
|
|
// prep conditions
|
|
QStringList prepCond;
|
|
QVariantList bindVal;
|
|
if(!conditions.isEmpty())
|
|
prepCond << " WHERE";
|
|
|
|
for(const auto& pair : conditions)
|
|
{
|
|
prepCond << pair.first+"=?";
|
|
bindVal << pair.second;
|
|
}
|
|
query.prepare(QString("SELECT %1 FROM %2%3%4").arg(sColumns,_table).arg(prepCond.join(" ")).arg(sOrder));
|
|
doAddBindValue(query, bindVal);
|
|
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed to get record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
|
|
// go to first row
|
|
query.next();
|
|
|
|
QSqlRecord rec = query.record();
|
|
for(int i = 0; i<rec.count(); i++)
|
|
{
|
|
results[rec.fieldName(i)] = rec.value(i);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DBManager::getRecords(QVector<QVariantMap>& results, const QStringList& tColumns, const QStringList& tOrder) const
|
|
{
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
query.setForwardOnly(true);
|
|
|
|
QString sColumns("*");
|
|
if(!tColumns.isEmpty())
|
|
sColumns = tColumns.join(", ");
|
|
|
|
QString sOrder("");
|
|
if(!tOrder.isEmpty())
|
|
{
|
|
sOrder = " ORDER BY ";
|
|
sOrder.append(tOrder.join(", "));
|
|
}
|
|
|
|
query.prepare(QString("SELECT %1 FROM %2%3").arg(sColumns,_table,sOrder));
|
|
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed to get records: '%s' in table: '%s' Error: %s", QSTRING_CSTR(sColumns), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
|
|
// iterate through all found records
|
|
while(query.next())
|
|
{
|
|
QVariantMap entry;
|
|
QSqlRecord rec = query.record();
|
|
for(int i = 0; i<rec.count(); i++)
|
|
{
|
|
entry[rec.fieldName(i)] = rec.value(i);
|
|
}
|
|
results.append(entry);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool DBManager::deleteRecord(const VectorPair& conditions) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(conditions.isEmpty())
|
|
{
|
|
Error(_log, "Oops, a deleteRecord() call wants to delete the entire table (%s)! Denied it", QSTRING_CSTR(_table));
|
|
return false;
|
|
}
|
|
|
|
if(recordExists(conditions))
|
|
{
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
|
|
// prep conditions
|
|
QStringList prepCond("WHERE");
|
|
QVariantList bindValues;
|
|
|
|
for(const auto& pair : conditions)
|
|
{
|
|
prepCond << pair.first+"=?";
|
|
bindValues << pair.second;
|
|
}
|
|
|
|
query.prepare(QString("DELETE FROM %1 %2").arg(_table,prepCond.join(" ")));
|
|
doAddBindValue(query, bindValues);
|
|
if(!query.exec())
|
|
{
|
|
Error(_log, "Failed to delete record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DBManager::createTable(QStringList& columns) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(columns.isEmpty())
|
|
{
|
|
Error(_log,"Empty tables aren't supported!");
|
|
return false;
|
|
}
|
|
|
|
QSqlDatabase idb = getDB();
|
|
// create table if required
|
|
QSqlQuery query(idb);
|
|
if(!tableExists(_table))
|
|
{
|
|
// empty tables aren't supported by sqlite, add one column
|
|
QString tcolumn = columns.takeFirst();
|
|
// default CURRENT_TIMESTAMP is not supported by ALTER TABLE
|
|
if(!query.exec(QString("CREATE TABLE %1 ( %2 )").arg(_table,tcolumn)))
|
|
{
|
|
Error(_log, "Failed to create table: '%s' Error: %s", QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
}
|
|
// create columns if required
|
|
QSqlRecord rec = idb.record(_table);
|
|
int err = 0;
|
|
for(const auto& column : columns)
|
|
{
|
|
QStringList id = column.split(' ');
|
|
if(rec.indexOf(id.at(0)) == -1)
|
|
{
|
|
if(!createColumn(column))
|
|
{
|
|
err++;
|
|
}
|
|
}
|
|
}
|
|
if(err)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DBManager::createColumn(const QString& column) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
if(!query.exec(QString("ALTER TABLE %1 ADD COLUMN %2").arg(_table,column)))
|
|
{
|
|
Error(_log, "Failed to create column: '%s' in table: '%s' Error: %s", QSTRING_CSTR(column), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBManager::tableExists(const QString& table) const
|
|
{
|
|
QSqlDatabase idb = getDB();
|
|
QStringList tables = idb.tables();
|
|
if(tables.contains(table))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool DBManager::deleteTable(const QString& table) const
|
|
{
|
|
if ( _readonlyMode )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(tableExists(table))
|
|
{
|
|
QSqlDatabase idb = getDB();
|
|
QSqlQuery query(idb);
|
|
if(!query.exec(QString("DROP TABLE %1").arg(table)))
|
|
{
|
|
Error(_log, "Failed to delete table: '%s' Error: %s", QSTRING_CSTR(table), QSTRING_CSTR(idb.lastError().text()));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DBManager::doAddBindValue(QSqlQuery& query, const QVariantList& variants) const
|
|
{
|
|
for(const auto& variant : variants)
|
|
{
|
|
QVariant::Type t = variant.type();
|
|
switch(t)
|
|
{
|
|
case QVariant::UInt:
|
|
case QVariant::Int:
|
|
case QVariant::Bool:
|
|
query.addBindValue(variant.toInt());
|
|
break;
|
|
case QVariant::Double:
|
|
query.addBindValue(variant.toFloat());
|
|
break;
|
|
case QVariant::ByteArray:
|
|
query.addBindValue(variant.toByteArray());
|
|
break;
|
|
default:
|
|
query.addBindValue(variant.toString());
|
|
break;
|
|
}
|
|
}
|
|
}
|