2019-07-12 16:54:26 +02:00
# include <db/DBManager.h>
# include <QSqlDatabase>
# include <QSqlError>
# include <QSqlQuery>
# include <QSqlRecord>
2019-07-16 23:11:53 +02:00
# include <QThreadStorage>
# include <QUuid>
2019-07-12 16:54:26 +02:00
# include <QDir>
2020-06-17 20:55:57 +02:00
# ifdef _WIN32
# include <stdexcept>
# endif
2019-07-12 16:54:26 +02:00
// not in header because of linking
static QString _rootPath ;
2019-07-16 23:11:53 +02:00
static QThreadStorage < QSqlDatabase > _databasePool ;
2019-07-12 16:54:26 +02:00
DBManager : : DBManager ( QObject * parent )
: QObject ( parent )
, _log ( Logger : : getInstance ( " DB " ) )
{
}
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
{
2019-07-16 23:11:53 +02:00
if ( _databasePool . hasLocalData ( ) )
return _databasePool . localData ( ) ;
2019-07-12 16:54:26 +02:00
else
{
2019-07-16 23:11:53 +02:00
auto db = QSqlDatabase : : addDatabase ( " QSQLITE " , QUuid : : createUuid ( ) . toString ( ) ) ;
_databasePool . setLocalData ( db ) ;
2019-07-12 16:54:26 +02:00
db . setDatabaseName ( _rootPath + " /db/ " + _dbn + " .db " ) ;
2019-07-16 23:11:53 +02:00
if ( ! db . open ( ) )
{
Error ( _log , QSTRING_CSTR ( db . lastError ( ) . text ( ) ) ) ;
throw std : : runtime_error ( " Failed to open database connection! " ) ;
}
return db ;
2019-07-12 16:54:26 +02:00
}
}
bool DBManager : : createRecord ( const VectorPair & conditions , const QVariantMap & columns ) const
{
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
{
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
{
QSqlDatabase idb = getDB ( ) ;
QSqlQuery query ( idb ) ;
query . setForwardOnly ( true ) ;
QString sColumns ( " * " ) ;
if ( ! tColumns . isEmpty ( ) )
sColumns = tColumns . 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 " ) . arg ( sColumns , _table ) . arg ( prepCond . join ( " " ) ) ) ;
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
{
QSqlDatabase idb = getDB ( ) ;
QSqlQuery query ( idb ) ;
query . setForwardOnly ( true ) ;
QString sColumns ( " * " ) ;
if ( ! tColumns . isEmpty ( ) )
sColumns = tColumns . join ( " , " ) ;
query . prepare ( QString ( " SELECT %1 FROM %2 " ) . arg ( sColumns , _table ) ) ;
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 ( 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 ( 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
{
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 ( 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 ;
}
}
}