From fe12aa6a4b54e3d204bee3aef3ab03d07dd06b29 Mon Sep 17 00:00:00 2001 From: Steven de Ridder Date: Mon, 24 Jan 2022 13:27:50 +0100 Subject: [PATCH] Initial commit. dependencies not resolved yet. --- .gitignore | 2 ++ CMakeLists.txt | 25 +++++++++++++++++++++++++ README.md | 0 src/CMakeLists.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dbconnectionwatchdog.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dbconnectionwatchdog.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dbconnector.cpp | 1248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dbconnector.h | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dbrelation.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/dbrelation.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 30 ++++++++++++++++++++++++++++++ 11 files changed, 2243 insertions(+), 0 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 src/CMakeLists.txt create mode 100644 src/dbconnectionwatchdog.cpp create mode 100644 src/dbconnectionwatchdog.h create mode 100644 src/dbconnector.cpp create mode 100644 src/dbconnector.h create mode 100644 src/dbrelation.cpp create mode 100644 src/dbrelation.h create mode 100644 tests/CMakeLists.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ff047c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c9b4a94 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0) + +# Check to see where cmake is located. +if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +elseif( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../cmake ) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) +else() + return() +endif() + +# Check to see if there is versioning information available +if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) + include(osdevversion) +endif() + +include(projectheader) +project_header(osdev_dbconnector) + +add_subdirectory(src) +add_subdirectory(tests) + +# include(packaging) +# package_component() diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..bf5607a --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.0) +include(projectheader) +project_header(dbconnector) + +find_package( Qt5Core REQUIRED ) +find_package( Qt5Sql REQUIRED ) + +include_directories( SYSTEM + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Sql_INCLUDE_DIRS} +) + +include(compiler) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../pugixml + ${CMAKE_CURRENT_SOURCE_DIR}/../logutils + ${CMAKE_CURRENT_SOURCE_DIR}/../config + ${CMAKE_CURRENT_SOURCE_DIR}/../dcxml + ${CMAKE_CURRENT_SOURCE_DIR}/../global +) + +set(SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/dbconnector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbrelation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dbconnectionwatchdog.cpp +) + +include(qtmoc) +create_mocs( SRC_LIST MOC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/dbconnector.h + ${CMAKE_CURRENT_SOURCE_DIR}/dbrelation.h + ${CMAKE_CURRENT_SOURCE_DIR}/dbconnectionwatchdog.h +) + +set_source_files_properties( + ${MOC_LIST} + PROPERTIES + COMPILE_FLAGS -Wno-undefined-reinterpret-cast +) + +link_directories( + ${CMAKE_BINARY_DIR}/lib +) + +include(library) +add_libraries( + ${Qt5Core_LIBRARIES} + ${Qt5Sql_LIBRARIES} + logutils + global + pugixml +) + +include(installation) +install_component() diff --git a/src/dbconnectionwatchdog.cpp b/src/dbconnectionwatchdog.cpp new file mode 100644 index 0000000..defc768 --- /dev/null +++ b/src/dbconnectionwatchdog.cpp @@ -0,0 +1,124 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + +#include "dbconnectionwatchdog.h" +#include "log.h" + +#include +#include + +using namespace osdev::components; + +DbConnectionWatchDog::DbConnectionWatchDog( int check_interval, QObject *_parent ) + : QObject( _parent ) + , m_pTimer( new QTimer( this ) ) + , m_check_interval( check_interval ) + , m_original_interval( check_interval ) + , m_dbc_valid( true ) +{ + + #if( QT_VERSION >= QT_VERSION_CHECK( 5, 7, 0) ) + { + QObject::connect( m_pTimer.data(), &QTimer::timeout, this, &DbConnectionWatchDog::slotStartConnectionCheck ); + } + #else + { + QObject::connect( m_pTimer.data(), SIGNAL( timeout() ), this, SLOT( slotStartConnectionCheck() ) ); + } + #endif + +} + +void DbConnectionWatchDog::slotStartConnectionCheck() +{ + LogDebug( "[DbConnectionWatchDog::slotStartConnectionCheck]", QString( "Checking Database Connection...." ) ); + // Get all the connections from QSqlDatabase + QStringList connection_names = QSqlDatabase::connectionNames(); + for( QString &conn_name : connection_names ) + { + QSqlDatabase dbConn = QSqlDatabase::database( conn_name, false ); + + + // Check to see if we can execute a query. Not all databases drivers report a database as "open" + // without actually accessing. It all depends on the Operating System and the connectiontype. + if( 0 == dbConn.tables().count() ) + { + // Check the error cause we didn't got any tablenames. + switch( dbConn.lastError().type() ) + { + case QSqlError::NoError: + LogWarning( "[DbConnectionWatchDog::slotStartConnectionCheck]", QString( "No Error or database unavailable on connection %1" ).arg( conn_name ) ); + dbConn.open(); + // Set the check interval to 1 second until the connection is back on. + m_check_interval = 1; + break; + + case QSqlError::ConnectionError: + LogWarning( "[DbConnectionWatchDog::slotStartConnectionCheck]", QString( "Connection Error on connection %1" ).arg( conn_name ) ); + dbConn.open(); + // Set the check interval to 1 second until the connection is back on. + m_check_interval = 1; + break; + case QSqlError::UnknownError: + LogWarning( "[DbConnectionWatchDog::slotStartConnectionCheck]", QString( "Unknown Error on connection %1" ).arg( conn_name ) ); + // Set the check interval to 1 second until the connection is back on. + m_check_interval = 1; + break; + + // The following Errors are not connection related but need to be here to satisfy the compiler. + case QSqlError::StatementError: + case QSqlError::TransactionError: + break; + default: + LogInfo( "[DbConnectionWatchDog::slotStartConnectionCheck]", QString( "Database connection Healthy...." ) ) + } + + if( m_dbc_valid ) + { + m_dbc_valid = false; + } + + emit signalDbConnected( conn_name, false ); + } + else + { + m_check_interval = m_original_interval; + if( !m_dbc_valid ) + { + LogInfo( "[DbConnectionWatchDog::slotStartConnectionCheck]", "Database connection restored.." ); + m_dbc_valid = true; + emit signalDbConnected( conn_name, m_dbc_valid ); + } + } + } + + // Restart the watchDog + this->start(); +} + +void DbConnectionWatchDog::start() +{ + m_pTimer->setInterval( m_check_interval * 1000 ); + m_pTimer->setSingleShot( true ); + + m_pTimer->start(); +} diff --git a/src/dbconnectionwatchdog.h b/src/dbconnectionwatchdog.h new file mode 100644 index 0000000..28a850b --- /dev/null +++ b/src/dbconnectionwatchdog.h @@ -0,0 +1,85 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + + +#ifndef OSDEV_COMPONENTS_DBCONNECTIONWATCHDOG_H +#define OSDEV_COMPONENTS_DBCONNECTIONWATCHDOG_H + +#include +#include +#include + +namespace osdev { +namespace components { +/*! + * \brief The DbConnectionWatchDog class checks on intervals if all registered database + * connections are still active. By using the simplest query form like fetching + * all tablenames on the connectionlevel, we trigger an error. + * Connection is checked every (check_interval) seconds. If an error is detected, + * check_interval is set to 1 seconds, reverting back to if the + * connection checks out again. + */ +class DbConnectionWatchDog : public QObject +{ + Q_OBJECT + +public: + /*! + * \brief The constructor + * \param check_interval in seconds. + * \param parent - The object that owns this connection guard. + */ + explicit DbConnectionWatchDog( int check_interval = 5, QObject *_parent = 0 ); + + ~DbConnectionWatchDog() {} + + /// Deleted Copy Constructor + DbConnectionWatchDog( const DbConnectionWatchDog& source ) = delete; + /// Deleted Assignment Operator + DbConnectionWatchDog& operator=( const DbConnectionWatchDog& ) = delete; + /// Deleted Move Constructor + DbConnectionWatchDog( DbConnectionWatchDog&& ) = delete; + /// Deleted Move Operator + DbConnectionWatchDog& operator=( DbConnectionWatchDog&& ) = delete; + + void start(); + +private: + QPointer m_pTimer; ///< The timer object this guard is based on. + int m_check_interval; ///< The current check_interval the connections are checked. + int m_original_interval; ///< The check_interval the connections are checked if present. + bool m_dbc_valid; ///< Private member reflecting the state. True for no problems, false for problems. + +signals: + void signalDbConnected( const QString& db_name, bool is_open ); + +private slots: + /*! + * \brief Slot being called if the timer reaches its timeout value, starting the connectioncheck. + */ + void slotStartConnectionCheck(); +}; + +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_DBCONNECTIONWATCHDOG_H */ diff --git a/src/dbconnector.cpp b/src/dbconnector.cpp new file mode 100644 index 0000000..6f84b24 --- /dev/null +++ b/src/dbconnector.cpp @@ -0,0 +1,1248 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + +#include "dbconnector.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dcxmlconfig.h" +#include "dbrelation.h" +#include "conversionutils.h" + +using namespace osdev::components; + +DbConnector::DbConnector() + : m_pWatchDog( QPointer() ) + , m_dataBase() + , m_sUserName() + , m_sPassword() + , m_sDatabaseName() + , m_sHostName() + , m_sDbType() + , m_sDbIdentifier() + , m_bDbOpen( false ) + , m_qhRelations() + , m_qhTableInfo() +{ + this->constructQueryHash(); +} + +DbConnector::DbConnector( const QString& sDbIdentifier ) + : m_pWatchDog( QPointer() ) + , m_dataBase() + , m_sUserName() + , m_sPassword() + , m_sDatabaseName() + , m_sHostName() + , m_sDbType() + , m_sDbIdentifier( sDbIdentifier ) + , m_bDbOpen( false ) + , m_qhRelations() + , m_qhTableInfo() +{ + this->constructQueryHash(); +} + +DbConnector::DbConnector( const QString& sUserName, + const QString& sPassword, + const QString& sDatabaseName, + const QString& sHostName, + const QString& sDbType, + const QString& sDbIdentifier ) + : m_pWatchDog( QPointer() ) + , m_dataBase() + , m_sUserName( sUserName ) + , m_sPassword( sPassword ) + , m_sDatabaseName( sDatabaseName ) + , m_sHostName( sHostName ) + , m_sDbType( sDbType ) + , m_sDbIdentifier( sDbIdentifier ) + , m_bDbOpen( false ) + , m_qhRelations() + , m_qhTableInfo() +{ + this->constructQueryHash(); +} + +DbConnector::DbConnector(const QHash& _qhCredentials ) + : DbConnector( _qhCredentials.value( "username" ), + _qhCredentials.value( "password" ), + _qhCredentials.value( "dbname" ), + _qhCredentials.value( "hostname" ), + _qhCredentials.value( "dbtype" ), + _qhCredentials.value( "identifier" ) ) +{ + this->constructQueryHash(); +} + +DbConnector::DbConnector( const DbConnector& source ) + : QObject( source.parent() ) + , m_pWatchDog( QPointer() ) + , m_dataBase( source.getDatabase() ) + , m_sUserName( source.getUsername() ) + , m_sPassword( source.getPassWord() ) + , m_sDatabaseName( source.getDatabaseName() ) + , m_sHostName( source.getHostName() ) + , m_sDbType( source.getDbType() ) + , m_sDbIdentifier( source.getDbIdentifier() ) + , m_bDbOpen( source.isOpen() ) + , m_qhRelations() + , m_qhTableInfo() +{ + this->constructQueryHash(); +} + +DbConnector::~DbConnector() +{ +} + +/**************************************************************************** + * C o n n e c t o r a n d G e t / S e t m e t h o d s + ****************************************************************************/ +bool DbConnector::connectDatabase() +{ + m_dataBase = QSqlDatabase::addDatabase( m_sDbType, m_sDbIdentifier ); + m_dataBase.setDatabaseName( m_sDatabaseName ); + m_dataBase.setHostName( m_sHostName ); + m_dataBase.setPassword( m_sPassword ); + m_dataBase.setUserName( m_sUserName ); + + m_bDbOpen = m_dataBase.open(); + + if( !m_bDbOpen ) + { + LogError( "[dbConnector::ConnectDatabase]", QString( "There was an error opening database %1 [dbConnector::ConnectDatabase] %2" ) + .arg( m_sDatabaseName).arg(m_dataBase.lastError().text() ) ); + + LogError( "[dbConnector::ConnectDatabase]", QString( "No use in continuing without a database. Ending %1" ).arg( QCoreApplication::applicationName() ) ); + } + else + { + LogInfo( "[dbConnector::ConnectDatabase]", "Database successfull connected" ); + } + + return m_bDbOpen; +} + +void DbConnector::setUserName( const QString& sUserName ) +{ + m_sUserName = sUserName; +} + +QString DbConnector::getUsername() const +{ + return m_sUserName; +} + +void DbConnector::setPassword(const QString& sPassword ) +{ + m_sPassword = sPassword; +} + +QString DbConnector::getPassWord() const +{ + return m_sPassword; +} + +void DbConnector::setDatabaseName( const QString& sDatabaseName ) +{ + m_sDatabaseName = sDatabaseName; +} + +QString DbConnector::getDatabaseName() const +{ + return m_sDatabaseName; +} + +void DbConnector::setHostName(const QString& sHostname ) +{ + m_sHostName = sHostname; +} + +QString DbConnector::getHostName() const +{ + return m_sHostName; +} + +void DbConnector::setDbType( const QString& sDbType ) +{ + m_sDbType = sDbType; +} + +QString DbConnector::getDbType() const +{ + return m_sDbType; +} + +void DbConnector::setDbIdentifier(const QString& sDbIdentifier ) +{ + m_sDbIdentifier = sDbIdentifier; +} + +QString DbConnector::getDbIdentifier() const +{ + return m_sDbIdentifier; +} + +QString DbConnector::getLastError() const +{ + return m_dataBase.lastError().text(); +} + +void DbConnector::setCredentials(const QHash &_qhCredentials ) +{ + m_sUserName = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbUsername ); + m_sPassword = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbPassword ); + m_sDatabaseName = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbName ); + m_sHostName = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbHostName ); + m_sDbType = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbType ); + m_sDbIdentifier = _qhCredentials.value( DCXmlConfig::eDbSettings::eDbIdentifier ); +} + +QSqlDatabase DbConnector::getDatabase() const +{ + return selectDatabase(); +} + +bool DbConnector::supportTransactions() const +{ + return this->selectDatabase().driver()->hasFeature( QSqlDriver::Transactions ); +} + +bool DbConnector::transactionBegin() +{ + return this->selectDatabase().transaction(); +} + +bool DbConnector::transactionCommit() +{ + return this->selectDatabase().commit(); +} + +bool DbConnector::transactionRollback() +{ + return this->selectDatabase().rollback(); +} + +/**************************************************************************** + * C R U D F u n c t i o n a l i t y + ****************************************************************************/ +bool DbConnector::createRecord( const QSqlRecord& record, const QString& sTable ) +{ + // Build the fieldsList and values list, based on the QSqlRecord. + // When done, call createRecord( sField, sValues, sTable ); + QStringList lstFields; + QStringList lstValues; + + for( int index = 0; index < record.count(); index++ ) + { + lstFields.append( this->quoteFieldName( record.fieldName( index ) ) ); + lstValues.append( this->quoteFieldValue( record.field(index).value() ) ); + } + + if( lstFields.isEmpty() || lstValues.isEmpty() ) + { + return false; + } + + return this->createRecord( lstFields.join( "," ), lstValues.join( "," ), this->quoteTableName( sTable ) ); +} + +bool DbConnector::createRecord(const QString& sFields, + const QString& sValues, + const QString& sTable ) +{ + bool bResult = false; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + QSqlQuery oQuery( mDb ); + bResult = oQuery.exec( "INSERT INTO " + sTable + + " ( " + sFields + " ) " + + "VALUES ( " + sValues + " ) " ); + if( !bResult ) + { + LogError("dbConnector::createRecord", + QString( "There was a problem adding a record : %1 [%2]" ) + .arg( oQuery.lastQuery() ) + .arg( oQuery.lastError().text() ) ); + } + else + { + LogDebug("dbConnector::createRecord", "Record saved to database."); + } + } + else + { + LogError("dbConnector::createRecord", + "Database is closed. Please connect first"); + } + + return true; // @todo : For now.. +} + +QSqlQuery* DbConnector::readRecord(const QString& sQuery ) +{ + QSqlQuery *pQuery = nullptr; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + pQuery = new QSqlQuery( sQuery, mDb ); + if( pQuery->exec() ) + { + LogError("dbConnector::readRecord", + "There was a problem reading a record : " + + pQuery->lastError().text()); + } + else + { + LogInfo("dbConnector::readRecord", "Record updated."); + } + } + else + { + LogError("dbConnector::readRecord", + "Database is closed. Please connect first"); + } + + return pQuery; +} + +QSqlQuery* DbConnector::readTableData( const QString& sTable ) const +{ + QSqlQuery *pQuery = nullptr; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + pQuery = new QSqlQuery( mDb ); + QString sQuery = QString( "SELECT * FROM %1" ).arg( this->quoteTableName( sTable) ); + + if ( !pQuery->exec( sQuery ) ) + { + LogError("dbConnector::readTableData", + "There was an error retrieving the tableData"); + LogError("dbConnector::readTableData", + pQuery->lastError().text()); + LogError("dbConnector::readTableData", + pQuery->lastQuery()); + } + } + + return pQuery; +} + +bool DbConnector::updateRecord(const QString& sIdNumber, + const QString& sIdField, + const QString& sValue, + const QString& sFieldName, + const QString& sTable ) +{ + bool bResult = false; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + QSqlQuery oQuery( mDb ); + bResult = oQuery.exec( "UPDATE " + this->quoteTableName(sTable) + + " SET " + sFieldName + " = " + sValue + + " WHERE " + sIdField + " = " + sIdNumber ); + if( !bResult ) + { + LogError("dbConnector::updateRecord", + "There was a problem updating a record : " + + oQuery.lastError().text()); + } + else + { + LogInfo("dbConnector::updateRecord", "Record updated."); + } + } + else + { + LogError("dbConnector::updateRecord", + "Database is closed. Please connect first"); + } + + return bResult; +} + +bool DbConnector::deleteRecord( const QString& sIdNumber, + const QString& sIdField, + const QString& sTable ) +{ + bool bResult = false; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + QSqlQuery oQuery( mDb ); + bResult = oQuery.exec( "DELETE FROM " + this->quoteTableName(sTable) + + " WHERE ( " + sIdField + ")" + + " = ( " + sIdNumber + " ) " ); + if( !bResult ) + { + LogError("dbConnector::deleteRecord", + "There was a problem deleting a record : " + + oQuery.lastError().text()); + } + else + { + LogInfo("dbConnector::deleteRecord", + "Record deleted from database."); + } + } + else + { + LogError("dbConnector::deleteRecord", + "Database is closed. Please connect first"); + } + + return bResult; +} + +/**************************************************************************** + * H E L P E R M E T H O D S + ****************************************************************************/ +bool DbConnector::clearTable( const QString& sTableName ) +{ + bool bResult = false; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + QSqlQuery oQuery( mDb ); + bResult = oQuery.exec( "DELETE FROM " + this->quoteTableName(sTableName) ); + if( !bResult ) + { + LogError("dbConnector::clearTable", + "There was a problem deleting the records : " + + oQuery.lastError().text()); + } + else + { + LogInfo("dbConnector::clearTable", "Table " + sTableName + " Emptied."); + bResult = true; + } + } + else + { + LogError("dbConnector::clearTable", + "Database is closed. Please connect first"); + } + + return bResult; +} + +int DbConnector::getNumRecords( const QString& sTable ) const +{ + int nResult = -1; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + QSqlQuery oQuery( mDb ); + if( oQuery.exec( "SELECT COUNT(*) as NumRecords FROM " + this->quoteTableName(sTable) ) ) + { + nResult = 0; + // Retrieve the Numbers of Records + while ( oQuery.next() ) + { + nResult += oQuery.record().value( "NumRecords" ).toInt(); + } + } + else + { + LogError("dbConnector::getNumRecords", + "There was a problem counting the records : " + + oQuery.lastError().text()); + } + } + else + { + LogError("dbConnector::getNumRecords", "Database is closed. Please connect first"); + } + + return nResult; +} + +bool DbConnector::recordsExist( const QString& sTable, const QString& sField, const QList& slFieldValues) const +{ + static const QString countQuery("SELECT COUNT(DISTINCT %2) as NumRecords FROM %1 WHERE %2 IN ( %3 )"); + if (slFieldValues.isEmpty()) + { + return true; + } + + QStringList valueList; + for ( const auto& v : slFieldValues) + { + valueList.append(this->toSqlValueString(v)); + } + QStringList uniqueValueList = valueList.toSet().toList(); + auto queryResult = this->executeQuery(countQuery + .arg(this->quoteTableName(sTable)) + .arg(this->quoteFieldName(sField)) + .arg(uniqueValueList.join(","))); + if (!queryResult) + { + return false; + } + + int nResult = 0; + while ( queryResult->next() ) + { + nResult += queryResult->record().value( "NumRecords" ).toInt(); + } + + return ( uniqueValueList.size() == nResult ); +} + +QStringList DbConnector::getTableNames() const +{ + QStringList lstTableNames; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + lstTableNames = mDb.tables(); + } + + return lstTableNames; +} + +QStringList DbConnector::getFieldNames( const QString& tableName ) const +{ + return QStringList( this->getFieldNamesAndTypes( tableName ).keys() ); +} + +QHash DbConnector::getFieldNamesAndTypes( + const QString& tableName ) const +{ + QHash qhResult; + QString _table; + + // Check if there is a 'dot' in the tablename. A Scheme is present which should be removed. + if( tableName.contains( "." ) ) + { + _table = stripSchemaFromTable( tableName ); + } + else + { + _table = tableName; + } + + // Build the correct query to get all the information of the given table. + QString sQuery = m_qhTableInfo.value( m_dataBase.driver()->dbmsType() ).arg( _table ); + if( !sQuery.isNull() && !sQuery.isEmpty() ) + { + QSqlQuery oQuery( this->selectDatabase() ); + if( oQuery.exec( sQuery ) ) + { + while( oQuery.next() ) + { + QString sFieldName = oQuery.value( 0 ).toString(); + QVariant::Type vFieldType = ConversionUtils::stringToQvarType( oQuery.value( 1 ).toString() ); + + qhResult.insert( sFieldName, vFieldType ); + } + } + else + { + // The query failed. Lets figure out why.. + LogDebug( "[DbConnector::getFieldNamesAndTypes]", QString( "The query reported an error : \n %1 for error : %2 \n" ) + .arg( oQuery.lastError().text() ) + .arg( sQuery ) ); + } + } + + return qhResult; +} + +QSqlDatabase DbConnector::selectDatabase() const +{ + QSqlDatabase dbResult; + + if ( !m_sDbIdentifier.isEmpty() ) + { + dbResult = QSqlDatabase::database( m_sDbIdentifier ); + } + else + { + dbResult = m_dataBase; + } + + return dbResult; +} + +bool DbConnector::associate( const QString& tableName, const QString& filterField, const QList& filterValueList, const QString& associateToField, const QVariant& associateToValue ) +{ + static const QString sQuery("UPDATE %1 SET %2 = %3 WHERE %4 IN ( %5 )"); + if (filterValueList.isEmpty()) + { + return true; + } + + QStringList valueList; + for (const auto& value : filterValueList) + { + valueList.append(this->toSqlValueString(value)); + } + const auto q = this->executeQuery( sQuery + .arg(this->quoteTableName(tableName)) + .arg(this->quoteFieldName(associateToField)) + .arg(this->toSqlValueString(associateToValue)) + .arg(this->quoteFieldName(filterField)) + .arg(this->toSqlValueString(filterValueList))); + if (q && !q->lastError().isValid()) + { + return true; + } + return false; +} + +bool DbConnector::disassociate( const QString& tableName, const QString& filterField, const QList& filterValueList, const QString& associateToField, const QVariant& associateToValue ) +{ + static const QString sDisassociateQuery("UPDATE %1 SET %2 = NULL WHERE %3 NOT IN ( %4 ) AND %2 = %5"); + static const QString sDisassociateAllQuery("UPDATE %1 SET %2 = NULL WHERE %3 = %4"); + QString sQuery; + if (filterValueList.isEmpty()) + { + sQuery = sDisassociateAllQuery + .arg(this->quoteTableName(tableName)) + .arg(this->quoteFieldName(associateToField)) + .arg(this->quoteFieldName(associateToField)) + .arg(this->toSqlValueString(associateToValue)); + } + else + { + QStringList valueList; + sQuery = sDisassociateQuery + .arg(this->quoteTableName(tableName)) + .arg(this->quoteFieldName(associateToField)) + .arg(this->quoteFieldName(filterField)) + .arg(this->toSqlValueString(filterValueList)) + .arg(this->toSqlValueString(associateToValue)); + } + + const auto q = this->executeQuery( sQuery ); + if (q && !q->lastError().isValid()) + { + return true; + } + return false; +} + +std::unique_ptr DbConnector::executeQuery( const QString& sQuery ) const +{ + std::unique_ptr pQuery; + + QSqlDatabase mDb = this->selectDatabase(); + if( mDb.isValid() && mDb.isOpen() ) + { + pQuery.reset(new QSqlQuery( sQuery, mDb )); + const auto err = pQuery->lastError(); + if(err.isValid()) + { + QString queryString = pQuery->executedQuery(); + if (queryString.isEmpty()) + { + queryString = pQuery->lastQuery(); + } + LogError("dbConnector::executeQuery", + QString( "Query '%1' gave error : '%2'" ) + .arg(queryString) + .arg(err.text())); + } + } + else + { + LogError("dbConnector::executeQuery", + "Database is closed. Please connect first"); + } + + return pQuery; +} + +QString DbConnector::toSqlValueString(const QVariant& value) const +{ + LogDebug("DbConnector::toSqlValueString", QString("Value is %1 (type is %2)").arg(value.toString()).arg(value.type())); + switch(value.type()) + { + case QVariant::Int: + return value.toString(); + case QVariant::Uuid: + return "'" + value.toString().replace( "{", "" ).replace( "}", "" ) + "'"; + case QVariant::List: + { + QStringList valueList; + for ( const auto& v : value.toList() ) + { + valueList.append( this->toSqlValueString( v ) ); + } + return valueList.join( "," ); + } + default: + return "'" + value.toString() + "'"; + } +} + +QHash > DbConnector::getRelations( const QStringList& tables ) +{ + QHash > qhResult; + /* First we get the type of database we're using. For each database type + * ( MySQL, PostGresQL or ORACLE ) the way of retrieving is different. + * We make sure the returned fields are always the same.. + */ + + // Check if the query database was build or not.. + if( 0 == m_qhRelations.count() ) + { + constructQueryHash(); + } + + // Return the query of our database... + QString relQuery = m_qhRelations.value( m_dataBase.driver()->dbmsType() ); + if( relQuery.isEmpty() ) /// Probably not a match or not implemented.. + { + return QHash>(); + } + else + { + // Retrieve the list of tables from the configuration : + QStringList tableList = tables; + if( tableList.isEmpty() && 0 == tableList.count() ) + { + tableList = this->getTableNames(); + } + + for( auto& table : tableList ) + { + qhResult.insert( table, getRelationByTableName( table ) ); + } + } + + return qhResult; +} + +void DbConnector::constructQueryHash() +{ + m_qhRelations.insert(QSqlDriver::UnknownDbms, ""); + m_qhRelations.insert(QSqlDriver::MSSqlServer, ""); + + m_qhRelations.insert(QSqlDriver::MySqlServer, "SELECT \ + TABLE_SCHEMA AS schema_name, \ + TABLE_NAME AS table_name, \ + CONSTRAINT_NAME AS constraint_name, \ + COLUMN_NAME AS index_column, \ + REFERENCED_TABLE_NAME AS foreign_table, \ + REFERENCED_COLUMN_NAME AS foreign_column \ + FROM \ + INFORMATION_SCHEMA.KEY_COLUMN_USAGE \ + WHERE \ + TABLE_SCHEMA = SCHEMA() \ + AND \ + TABLE_NAME = '%1' \ + AND \ + REFERENCED_TABLE_NAME IS NOT NULL" ); + + // @todo : Different schemas in the same database, with the same constraints will screw this up... + m_qhRelations.insert(QSqlDriver::PostgreSQL, "SELECT DISTINCT \ + n.nspname AS schema_name, \ + pc.relname AS table_name, \ + r.conname AS constraint_name, \ + pc1.relname AS foreign_table, \ + r.contype AS constraint_type, \ + pg_catalog.pg_get_constraintdef(r.oid) as definition \ + FROM \ + pg_constraint AS r \ + JOIN pg_catalog.pg_namespace n on n.oid = r.connamespace \ + JOIN pg_class AS pc ON pc.oid = r.conrelid \ + LEFT JOIN pg_class AS pc1 ON pc1.oid = r.confrelid \ + WHERE \ + r.contype = 'f' \ + AND \ + pc.relname = '%1' \ + ORDER BY \ + schema_name, \ + table_name, \ + foreign_table" ); + + m_qhRelations.insert(QSqlDriver::Oracle, ""); + m_qhRelations.insert(QSqlDriver::Sybase, ""); + m_qhRelations.insert(QSqlDriver::SQLite, ""); + m_qhRelations.insert(QSqlDriver::Interbase, ""); + m_qhRelations.insert(QSqlDriver::DB2, "" ); + + // ============================================================================================================ + // Build the table Information Query Hash + // ============================================================================================================ + + m_qhTableInfo.insert(QSqlDriver::UnknownDbms, ""); + m_qhTableInfo.insert(QSqlDriver::MSSqlServer, ""); + m_qhTableInfo.insert(QSqlDriver::MySqlServer, "SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = '%1'" ); + m_qhTableInfo.insert(QSqlDriver::PostgreSQL, "SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = '%1'" ); + m_qhTableInfo.insert(QSqlDriver::Oracle, ""); + m_qhTableInfo.insert(QSqlDriver::Sybase, ""); + m_qhTableInfo.insert(QSqlDriver::SQLite, ""); + m_qhTableInfo.insert(QSqlDriver::Interbase, ""); + m_qhTableInfo.insert(QSqlDriver::DB2, "" ); +} + +QList DbConnector::getRelationByTableName( const QString& tableName ) +{ + QList qlResult; + + if ( 0 == m_qhRelations.count() || 0 == m_qhTableInfo.count() ) + { + this->constructQueryHash(); + } + + // Return the query of our database... + QString relQuery = m_qhRelations.value( m_dataBase.driver()->dbmsType() ); + if( !relQuery.isEmpty() ) + { + QSqlQuery oQuery( this->selectDatabase() ); + if( oQuery.exec( relQuery.arg( stripSchemaFromTable( tableName ) ) ) ) + { + while( oQuery.next() ) + { + DbRelation *pRel = new DbRelation(); + pRel->setSchemaName( oQuery.record().value( "schema_name" ).toString() ); + pRel->setTableName( oQuery.record().value( "table_name" ).toString() ); + pRel->setConstraintName( oQuery.record().value( "constraint_name" ).toString() ); + + switch ( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + pRel->setIndexColumn( "" ); + break; + case QSqlDriver::PostgreSQL: + pRel->setIndexColumn( + parseDefinition( + oQuery.record().value( "definition" ).toString() ) ); + + // Get schema name associated with the foreign table, instead of the target-table. + pRel->setForeignTable( getSchemaByTable( oQuery.record().value( "foreign_table" ).toString() ) ); + + break; + case QSqlDriver::MySqlServer: + pRel->setIndexColumn( oQuery.record().value("index_column").toString() ); + pRel->setForeignTable( oQuery.record().value( "foreign_table" ).toString() ); + break; + + } + pRel->setForeignPrimKey( this->getPrimaryKey( oQuery.record().value( "foreign_table" ).toString() ) ); + + qlResult.append( pRel ); + LogDebug( "[DbConnector::getRelationByTableName]", QString( "Relation->schemaName : %1" ).arg( pRel->schemaName() ) ); + LogDebug( "[DbConnector::getRelationByTableName]", QString( "Relation->tableName : %1" ).arg( pRel->tableName() ) ); + LogDebug( "[DbConnector::getRelationByTableName]", QString( "Relation->constraintName : %1" ).arg( pRel->constraintName() ) ); + LogDebug( "[DbConnector::getRelationByTableName]", QString( "Relation->foreignTable : %1" ).arg( pRel->foreignTable() ) ); + LogDebug( "[DbConnector::getRelationByTableName]", QString( "Relation->indexColumn : %1" ).arg( pRel->indexColumn() ) ); + } + } + else + { + LogInfo( "[DbConnector::getRelationByTableName]", QString( "There were no relations for table : %1" ).arg( tableName ) ); + LogInfo( "[DbConnector::getRelationByTableName]", QString( "Query executed : %1" ).arg( oQuery.lastQuery() ) ); + LogInfo( "[DbConnector::getRelationByTableName]", QString( "Query reported the following error : %1" ).arg( oQuery.lastError().text() ) ); + } + } + else + { + LogInfo( "[DbConnector::getRelationByTableName]", QString( "No query was found for database type : %1" ).arg( m_dataBase.driverName() ) ); + } + + return qlResult; +} + +QString DbConnector::parseDefinition( const QString& definition ) const +{ + QString _definition = definition; + QString leftPattern = "FOREIGN KEY ("; + QString rightPattern = ") REFERENCES"; + + QString noLeft = _definition.replace( leftPattern, "" ); + + return noLeft.left( noLeft.indexOf( rightPattern ) ).replace( "\"", "" ); +} + +QString DbConnector::stripSchemaFromTable( const QString& tableName ) const +{ + QString sResult = tableName; + + if( tableName.contains(".") ) + { + sResult = tableName.right( tableName.size() - tableName.indexOf( "." ) - 1 ).replace("\"", ""); + } + + return sResult; +} + +QVariant DbConnector::getRelationKey( const QString& tableName, + const QString& filterField, + const QVariant& filterValue, + const QString& foreignPrimKey ) const +{ + QString l_primKey; + if( foreignPrimKey.isNull() || foreignPrimKey.isEmpty() ) + { + // Retrieve the primary key of the given table. + l_primKey = this->getPrimaryKey( tableName ); + } + else + { + l_primKey = foreignPrimKey; + } + + // If none was found, return an empty QVariant + if ( l_primKey.isNull() ) + { + LogInfo( "[DbConnector::getRelationKey]", + QString( "There was no primary keyfield found for : %1" ) + .arg( tableName ) ); + return QVariant(); + } + + // Build the QueryString with the parameters given : + // %1 - Primary Key ( l_primKey ) + // %2 - tableName (Quoted if needed) + // %3 - filterField + // %4 - filterValue + QString sQuery = QString( "SELECT \"%1\" FROM %2 WHERE \"%3\" = '%4'" ) + .arg( l_primKey, this->quoteTableName(tableName), filterField, filterValue.toString().replace("{", "").replace( "}", "" ) ); + + QVariant l_resPK; + QSqlQuery oQuery( m_dataBase ); + if( oQuery.exec( sQuery ) && oQuery.first() && oQuery.size() == 1 ) + { + l_resPK = oQuery.value( l_primKey ); + } + else if( oQuery.size() > 1 ) + { + LogInfo( "[DbConnector::getRelationKey]", + QString( "Multiple records found for value : %1" ) + .arg( filterValue.toString() ) ); + } + else + { + LogInfo( "[DbConnector::getRelationKey]", + QString( "There was no primary key retrieved for : %1" ) + .arg( sQuery ) ); + } + return l_resPK; +} + +QString DbConnector::getPrimaryKey( const QString& tableName ) const +{ + switch( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::MySqlServer: + return this->getPrimaryKeyMySQL( tableName ); + case QSqlDriver::PostgreSQL: + return this->getPrimaryKeyPostGreSQL( tableName ); + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + break; + } + return QString(); +} + +QString DbConnector::getPrimaryKeyMySQL( const QString& tableName ) const +{ + QString sQuery = QString( "SHOW INDEX FROM %1 WHERE Key_name = '%2'" ) + .arg( tableName ) + .arg( m_dataBase.primaryIndex( this->quoteTableName( tableName ) ).name() ); + + QSqlQuery oQuery( m_dataBase ); + if( oQuery.exec( sQuery ) && oQuery.first() && oQuery.size() == 1 ) + { + return oQuery.value( "Column_name" ).toString(); + } + + return QString(); +} + +QString DbConnector::getPrimaryKeyPostGreSQL( const QString& tableName ) const +{ + QString sResult; + QString _table; + if( tableName.contains( "." ) ) + { + _table = this->stripSchemaFromTable( tableName ); + } + else + { + _table = tableName; + } + + QString sQuery = QString("SELECT \ + c.column_name AS key_field, \ + c.data_type AS key_type, \ + tc.table_name AS table_name \ + FROM \ + information_schema.table_constraints tc \ + JOIN \ + information_schema.constraint_column_usage AS ccu \ + USING (constraint_schema, constraint_name) \ + JOIN \ + information_schema.columns AS c ON c.table_schema = tc.constraint_schema \ + AND \ + tc.table_name = c.table_name AND ccu.column_name = c.column_name \ + WHERE \ + constraint_type = 'PRIMARY KEY' \ + AND \ + tc.table_name = '%1'").arg( _table ); + + QSqlQuery oQuery( m_dataBase ); + if( oQuery.exec( sQuery ) ) + { + // Select the first record. + oQuery.first(); + LogDebug( "[DbConnector::getPrimaryKeyPostGreSQL]", + QString( "KeyField found.. : %1" ).arg( oQuery.value( "key_field" ).toString() ) ); + sResult = oQuery.value( "key_field" ).toString(); + } + else + { + LogInfo( "[DbConnector::getPrimaryKeyPostGreSQL]", + QString( "There was an error running the query : %1" ).arg( sQuery ) ); + LogInfo( "[DbConnector::getPrimaryKeyPostGreSQL]", + QString( "Dblayer gave error : %1" ).arg( oQuery.lastError().text() ) ); + } + + return sResult; +} + +QString DbConnector::quoteTableName( const QString& tableName ) const +{ + QString sResult( tableName ); + switch( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::MySqlServer: + return sResult; + + case QSqlDriver::PostgreSQL: + if( -1 == sResult.indexOf( "\"" ) ) + { + // Ok. Seems like it is not quoted. + sResult = QString( "\"" + QString( sResult.replace('.', "\".\"" ) + "\"") ); + return sResult; + } + else + { + return sResult; + } + + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + LogInfo( "[OrmHandler::quoteTableName]", + QString( "Unknown database requested : %1" ) + .arg( m_dataBase.driverName() ) ); + break; + } + return sResult; +} + +QString DbConnector::quoteFieldName( const QString& fieldName ) const +{ + QString sResult( fieldName ); + switch( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::MySqlServer: + return sResult; + + case QSqlDriver::PostgreSQL: + if( -1 == sResult.indexOf( "\"" ) ) + { + // Ok. Seems like it is not quoted. + sResult = QString( "\"%1\"" ).arg(fieldName); + return sResult; + } + else + { + return sResult; + } + + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + LogInfo( "[OrmHandler::quoteFieldName]", + QString( "Unknown database requested : %1" ) + .arg( m_dataBase.driverName() ) ); + break; + } + return sResult; +} + +QString DbConnector::quoteFieldValue( const QVariant& value ) const +{ + QString sResult( value.toString() ); + switch( value.type() ) + { + case QVariant::Uuid: + case QVariant::Date: + case QVariant::DateTime: + case QVariant::String: + sResult = QString( "%1%2%1" ).arg( quoteChar() ).arg( sResult ); + break; + default: + break; + } + + return sResult; +} + +QString DbConnector::quoteChar() const +{ + QString sResult = ""; + switch( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::MySqlServer: + return sResult; + + case QSqlDriver::PostgreSQL: + sResult = QString( "'" ); + return sResult; + + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + LogInfo( "[OrmHandler::quoteFieldName]", + QString( "Unsupported database requested : %1" ) + .arg( m_dataBase.driverName() ) ); + break; + } + return sResult; +} + +QString DbConnector::getSchemaByTable( const QString& tableName ) const +{ + QString sResult; + QString sQuery; + + switch( m_dataBase.driver()->dbmsType() ) + { + case QSqlDriver::PostgreSQL: + sQuery = QString( "SELECT \ + table_schema, \ + table_name \ + FROM \ + information_schema.tables \ + WHERE \ + table_name = '%1'" ) + .arg( tableName ); + break; + case QSqlDriver::MySqlServer: + case QSqlDriver::UnknownDbms: + case QSqlDriver::MSSqlServer: + case QSqlDriver::Oracle: + case QSqlDriver::Sybase: + case QSqlDriver::SQLite: + case QSqlDriver::Interbase: + case QSqlDriver::DB2: + LogInfo( "[OrmHandler::quoteTableName]", + QString( "Unknown database requested : %1" ) + .arg( m_dataBase.driverName() ) ); + break; + } + + // If no joy, return an empty string. + if( sQuery.isEmpty() ) + { + return QString(); + } + + // Geronimo! + QSqlQuery oQuery( m_dataBase ); + if( oQuery.exec( sQuery ) ) + { + if( oQuery.first() ) + { + sResult = oQuery.value( "table_schema" ).toString() + QString( "." ) + tableName; + } + + } + return sResult; +} + +void DbConnector::slotDbConnected( const QString& db_name, bool is_open ) +{ + // We're not using the name (yet) already there for future implementations. + Q_UNUSED( db_name ); + m_bDbOpen = is_open; +} + +void DbConnector::startDbWatchDog( const int check_interval ) +{ + if( m_pWatchDog.isNull() ) + { + m_pWatchDog = new DbConnectionWatchDog( check_interval, this ); + #if( QT_VERSION >= QT_VERSION_CHECK( 5, 2, 0) ) + connect( m_pWatchDog.data(), &DbConnectionWatchDog::signalDbConnected, this, &DbConnector::slotDbConnected ); + #else + connect( m_pWatchDog.data(), SIGNAL( signalDbConnected( const QString&, bool ) ), this, SLOT( slotDbConnected( const QString& bool ) ) ); + #endif + } + + if( m_pWatchDog ) + { + m_pWatchDog->start(); + LogInfo( "[DbConnector::startDbWatchDog]", QString( "Database Watchdog started with interval : %1" ).arg( check_interval ) ); + } +} diff --git a/src/dbconnector.h b/src/dbconnector.h new file mode 100644 index 0000000..ecb432b --- /dev/null +++ b/src/dbconnector.h @@ -0,0 +1,499 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + +#ifndef OSDEV_COMPONENTS_DBCONNECTOR_H +#define OSDEV_COMPONENTS_DBCONNECTOR_H + +#include "dcxmlconfig.h" +#include "dbconnectionwatchdog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +class QSqlQuery; +class QString; +class QStringList; +template class QHash; +class QSqlDatabase; + +namespace osdev { +namespace components { + +class DbRelation; + +/** + * @brief Database connector + */ +class DbConnector : public QObject +{ + Q_OBJECT + +public: + + /*! Default Ctor */ + DbConnector(); + + /** + * @brief Ctor with DatabaseIdentifier + * @param sDbIdentifier Database identifier + */ + DbConnector(const QString& sDbIdentifier); + + /*! + * @brief Constructor with connection information + * @param sUserName Username + * @param sPassword Password + * @param sDatabaseName Name of the database + * @param sHostName Name of the host + * @param sDbType Type of database. Could be one of the string, supported by QSQLDriver. + * @param sDbIdentifier Used internally to distinguish between connections. + * If given and already existing, it will be overwritten, + * else a new connection will be created. + */ + DbConnector(const QString& sUserName, + const QString& sPassword, + const QString& sDatabaseName, + const QString& sHostName, + const QString& sDbType, + const QString& sDbIdentifier ); + + /// copy-constructor + DbConnector( const DbConnector& source ); + /// assignment operator + DbConnector& operator=( const DbConnector& ) = delete; + /// move-constructor + DbConnector( DbConnector&& ) = delete; + /// move operator + DbConnector& operator=(DbConnector&&) = delete; + + /** + * @brief Ctor with connection Information packed in a nice QHash + * @param _qhCredentials Credentials + */ + DbConnector( const QHash& _qhCredentials ); + + /*! Dtor */ + virtual ~DbConnector(); + + /** + * @brief Connect to the database + * @return True if connection is succesful, false otherwise + */ + bool connectDatabase(); + + /** + * @brief Set the username + * @param sUserName Username + */ + void setUserName(const QString& sUserName ); + + /// @return Current username + QString getUsername() const; + + /** + * @brief Set the password + * @param sPassword Password + */ + void setPassword( const QString& sPassword ); + + /// @return Current password + QString getPassWord() const; + + /** + * @brief Set the database name + * @param sDatabaseName Database name + */ + void setDatabaseName(const QString& sDatabaseName ); + + /// @return Current database name + QString getDatabaseName() const; + + /** + * @brief Set the hostname where the database resides + * @param sHostname Hostname + */ + void setHostName( const QString& sHostname ); + + /// @return Current hostname + QString getHostName() const; + + /** + * @brief Set the type of database + * @param sDbType Type of database + */ + void setDbType(const QString& sDbType ); + + /// @return Current type of database + QString getDbType() const; + + /** + * @brief Set the database identifier + * @param sDbIdentifier Database identifier + */ + void setDbIdentifier( const QString& sDbIdentifier ); + + /// @return Current database identifier + QString getDbIdentifier() const; + + /// @return Get the last error that occurred + QString getLastError() const; + + /** + * @brief Set credentials from a configuration + * @param _qhCredentials Hash containing the credentials + */ + void setCredentials(const QHash& _qhCredentials ); + + /// @return Current QSqlDatabase instance + QSqlDatabase getDatabase() const; + + /*! + * \brief Indicates wether the database supports transactions. + * \return True if so. False if not, or the database is closed (will check). + */ + bool supportTransactions() const; + + /*! + * \brief Implemented for convenience. This will start a transaction. + * (See also supportTransactions() before calling this method ); + * \return True if successful( i.e. If the driver supports it). False if it failed. + */ + bool transactionBegin(); + + /*! + * \brief Implemented for convenience. This will commit a transaction. + * (See also supportTransactions() before calling this method ); + * \return True if successful( i.e. If the driver supports it). False if it failed. + */ + bool transactionCommit(); + + /*! + * \brief Implemented for convenience. This will rollback a transaction. + * (See also supportTransactions() before calling this method ); + * \return True if successful( i.e. If the driver supports it). False if it failed. + */ + bool transactionRollback(); + + /** + * @brief Creates a new record. + * @param record to insert into sTable. All field info and values are housed + * inside the object...... + * @return True on success. False on Failure. + */ + bool createRecord( const QSqlRecord& record, const QString& sTable ); + + /** + * @brief Create a new record + * @param sFields Comma-separated list of fields + * @param sValues Comma-seperated list of value + * @param sTable Database table to change + * @return True on success. False on failure. + */ + bool createRecord( const QString& sFields, + const QString& sValues, + const QString& sTable ); + + /** + * @brief Perform a read-query on the database. + * @param sQuery SQL query to perform + * @return QSqlQuery object pointer containing the result of the query. + * The caller is responsible for cleaning up the object after processing. + */ + QSqlQuery* readRecord( const QString& sQuery ); + + /** + * @brief Update a field in an existing record + * @param sIdNumber Value to match to locate the record + * @param sIdField Field that must contain sIdNumber + * @param sValue New value + * @param sFieldName Field name in which so store sValue + * @param sTable Table in which to search/replace + * @return True on success. False on failure, use getLastError() to retrieve + * the reason for failure + */ + bool updateRecord( const QString& sIdNumber, + const QString& sIdField, + const QString& sValue, + const QString& sFieldName, + const QString& sTable ); + + /** + * @brief Delete the selected record + * @param sIdNumber Value to match to locate the record + * @param sIdField Field that must contain sIdNumber + * @param sTable Table in which to delete + * @return True on success. False on failure, use getLastError() to retrieve + * the reason for failure + */ + bool deleteRecord(const QString& sIdNumber, + const QString& sIdField, + const QString& sTable ); + + // Helper methods + /** + * @brief Clear a whole table + * @param sTableName Name of the table + * @return True on success. False on failure, use getLastError() to retrieve + * the reason for failure + */ + bool clearTable( const QString& sTableName ); + + /** + * @brief Read all data for a specific table + * @param sTable Table to read + * @return QSqlQuery object pointer containing the result of the query. + * The caller is responsible for cleaning up the object after processing. + */ + QSqlQuery* readTableData( const QString& sTable ) const; + + /** + * @brief Gets the number of records for the specified table + * @param sTable Table to read + * @return Number of records in that table + */ + int getNumRecords( const QString& sTable ) const; + + /** + * @brief Check if the given list of values for a specific field in a specific table are found as records in the database. + * @param sTable Table to query. + * @param sField The field name for which the values are checked. + * @param slFieldValues The values that are searched for. + * @return true if all values are found or the slFieldValues list was empty, false otherwise. + */ + bool recordsExist( const QString& sTable, const QString& sField, const QList& slFieldValues) const; + + // Specific Meta-Data functionality + /** + * @brief Retrieve all table names + * @return List of table names + */ + QStringList getTableNames() const; + + /*! + * \brief Removes the Schema from the variable. It searches + * for a pattern like . and chops off + * the part before (including the) point. + * \param tableName - The name of the table (including the schemaname). + * \return Table name. Check for .isEmpty() for success. + */ + QString stripSchemaFromTable( const QString& tableName ) const; + + /** + * @brief Retrieve the field-names for a table + * @param tableName Name of the table + * @return List of fields + */ + QStringList getFieldNames( const QString& tableName ) const; + + /** + * @brief Retrieve the field-names and types for a table + * @param tableName Name of the table + * @return Hash of field-name => type + */ + QHash getFieldNamesAndTypes( + const QString& tableName ) const; + + /*! + * \brief Retrieves the relations as used between two tables. + * \return A Hash containing the relations by tablename. + */ + QHash > getRelations( const QStringList& tables = QStringList() ); + + /*! + * \brief Get the relations belonging to the given table name. + * \param tableName The table we like to get the constraints from. + * \return A List of all relation objects. Ownership of the pointers is + * transferred to the caller. The caller is responsible for cleaning up the objects. + */ + QList getRelationByTableName( const QString& tableName ); + + /*! + * \brief Retrieve a primaryKey, based on the filter settings + * \param tableName - The table we want the primary key from. + * \param filterField - The fieldname we like to filter on. + * \param filterValue - The value we like to filter on. + * \return The value of the primary key on success or an empty QVariant + */ + QVariant getRelationKey(const QString& tableName, const QString& filterField, const QVariant& filterValue , const QString &foreignPrimKey = QString() ) const; + + /*! + * \brief Gets the primary key from the given table + * \param tableName - The table we would like the primary key from. + * \return The name of the key-field of the given table. It will return an empty string + * if the table doesn't have a primary key or the table doesn't exist. + */ + QString getPrimaryKey( const QString& tableName ) const; + + /*! + * \brief Some databases are quite picky when it comes to tables and scheme's + * Based on the database we have connected, we quote the tablename + * to satisfy the db-driver. + * \param tableName - the tablename as given from the database layer. + * \return The quoted tablename. + */ + QString quoteTableName( const QString& tableName ) const; + + /*! + * \brief Quote a fieldname if necessary. + * Based on the database we have connected, we quote the fieldname + * to satisfy the db-driver. + * \param fieldName - the fieldname to quote. + * \return The quoted fieldname. + */ + QString quoteFieldName(const QString& fieldName ) const; + + /*! + * \brief Quote a fieldName if the type demands it. + * \param The value as QVariant. Based on its type it will receive quotes around it. + * \return The quote fieldValue + */ + QString quoteFieldValue( const QVariant& value ) const; + + /*! + * \brief Each database has its own "quircks" when it comes to quotes. + * Based on the database type, the correct quote-character is returned + * \return Th quote-character based on the database type. + */ + QString quoteChar() const; + + /*! + * \brief Some databases want to use the schema in front af the table name. + * \param tableName - The name of the table we want to retrieve the schema for. + * \return The Schema name as QString or an empty QString if unsuccessfull + */ + QString getSchemaByTable( const QString& tableName ) const; + + /*! + * \brief Select the database on which to perform operations + * \return Selected database + */ + QSqlDatabase selectDatabase() const; + + /*! + * \brief Associate a list of records to a specific foreign record. + * \param tableName The table to use. + * \param filterField The fieldname that is filtered on. + * \param filterValueList A comma separated list of filter values that determine the set of records the assocation is set to. + * \param associateToField The foreign key field. + * \param associateToValue The foreign record that the list of records is associated to. + * \return true if assocation is successful, false if not. + */ + bool associate( const QString& tableName, const QString& filterField, const QList& filterValueList, const QString& associateToField, const QVariant& associateToValue ); + + /*! + * \brief Disassociate records that do not belong to the given list but do have an assocation to the given foreign record. + * \param tableName The table to use. + * \param filterField The fieldname that is filtered on. + * \param filterValueList A comma separated list of filter values. + * The records that do have an assocation to the given foreign record and are not part of the list will be disassociated. + * \param associateToField The foreign key field. + * \param associateToValue The foreign record from which records that are not part of the given list are disassociated from. + * \return true if disassocation is successful, false if not. + */ + bool disassociate( const QString& tableName, const QString& filterField, const QList& filterValueList, const QString& associateToField, const QVariant& associateToValue ); + + /*! + * \brief Start a database connection watchdog. On certain times (5 secs default) all database connections are checked and reopened if necessary. + */ + void startDbWatchDog( const int check_interval ); + + /*! + * \brief Indicates the current database open or closed. It will be set by the watchdog during operation and the connection method during the initialization. + * \return True if database is open, False if not. If it is not supported by the database connection, we assume always True. The error from the database driver should be checked. + */ + bool isOpen() const { return m_bDbOpen; } + +private: + /*! + * \brief Execute a query on a database. + * \param sQuery Query to execute. + * \return A pointer to the executed QSqlQuery. + * \retval nullptr Database is not connected to or is not valid. + * \note The caller is responsible for disposing of the returned QSqlQuery object before the database is destroyed! + */ + std::unique_ptr executeQuery( const QString& sQuery ) const; + + /*! + * \brief Convert a QVariant to an Sql value string. + * \return Sql representation of the value. + */ + QString toSqlValueString(const QVariant& value) const; + + /*! + * \brief Retrieve the Primary key-field from the a specific table + * in a MySQL Database. Called by getPrimaryKey.. + * \param tableName - The name of the table we're investigating. + * \return The primary key field name for the specified the table name. + */ + QString getPrimaryKeyMySQL( const QString& tableName ) const; + + /*! + * \brief Retrieve the Primary key-field from the a specific table + * in a PostGreSQL Database. Called by getPrimaryKey.. + * \param tableName - The name of the table we're investigating. + * \return The primary key field name for the specified the table name. + */ + QString getPrimaryKeyPostGreSQL( const QString& tableName ) const; + + /** + * @brief Parses the specified definition. + * @param definition The definition to parse. + * @return the indexColumn as defined in the definition (Pleonasm someone?) + */ + QString parseDefinition( const QString& definition ) const; + + /** + * @brief Build the hash table, containing the relation queries for each type of database. + */ + void constructQueryHash(); + + QPointer m_pWatchDog; + + QSqlDatabase m_dataBase; ///< Used database when a DbIdentifier is specified + + QString m_sUserName; ///< Username + QString m_sPassword; ///< Password + QString m_sDatabaseName; ///< Name of the database + QString m_sHostName; ///< Host on which the database runs + QString m_sDbType; ///< Type of database + QString m_sDbIdentifier; ///< Identifier for the database + bool m_bDbOpen; ///< Identifying the database open or closed. + + QHash m_qhRelations; ///< Hash used to store the Relations queries by database type. + QHash m_qhTableInfo; ///< Hash used to store the TableMetaInfo queries by database type. + +private slots: + void slotDbConnected( const QString& db_name, bool is_open ); + +}; + +} // End namespace components +} // End namespace osdev + +#endif /* OSDEV_COMPONENTS_DBCONNECTOR_H */ diff --git a/src/dbrelation.cpp b/src/dbrelation.cpp new file mode 100644 index 0000000..927fdfe --- /dev/null +++ b/src/dbrelation.cpp @@ -0,0 +1,39 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + + +#include "dbrelation.h" + +#include + +using namespace osdev::components; + +DbRelation::DbRelation( QObject *_parent ) + : QObject( _parent ) + , m_schemaName( "" ) + , m_tableName( "" ) + , m_constraintName( "" ) + , m_foreignTable( "" ) + , m_indexColumn( "" ) + , m_foreignPrimKey( "" ) +{ +} diff --git a/src/dbrelation.h b/src/dbrelation.h new file mode 100644 index 0000000..4b48af3 --- /dev/null +++ b/src/dbrelation.h @@ -0,0 +1,135 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ + +#ifndef OSDEV_COMPONENTS_DBRELATION_H +#define OSDEV_COMPONENTS_DBRELATION_H + +#include +#include + +namespace osdev { +namespace components { + +/*! + * \brief The DbRelation class is a container class holding the + * information regarding the relation between two tables. + */ + +class DbRelation : public QObject +{ + Q_OBJECT + +public: + DbRelation( QObject *parent = nullptr ); + + /*! + * \brief Get the name of the database scheme + * \return The schemaname as QString. isEmpty() is true if none was set. + */ + const QString& schemaName() const { return m_schemaName; } + + /*! + * \brief Set the name of the database scheme + * \param _schemaName - The name of the schema. + */ + void setSchemaName( const QString &_schemaName ) { m_schemaName = _schemaName; } + + /*! + * \brief Get the name of the table this relation describes. + * \return The name of the table this relation describes. + */ + const QString& tableName() const { return m_tableName; } + + /*! + * \brief Set the name of the table this relation describes. + * \param _tableName - The name of the table this relation describes. + */ + void setTableName( const QString &_tableName ) { m_tableName = _tableName; } + + /*! + * \brief Get the name of the constraint this relation describes. + * \return the name of the constraint this relation describes. + */ + const QString& constraintName() const { return m_constraintName; } + + /*! + * \brief Set the name of the constraint this relation describes. + * \param _constraintName - The name of the constraint this relation describes. + */ + void setConstraintName( const QString &_constraintName ) { m_constraintName = _constraintName; } + + /*! + * \brief Get the name of the related table this relation describes. + * \return the name of the related table this relation describes. + */ + const QString& foreignTable() const { return m_foreignTable; } + + /*! + * \brief Set the name of the related table this relation describes. + * \param _foreignTable - the name of the related table this relation describes. + */ + void setForeignTable( const QString &_foreignTable ) { m_foreignTable = _foreignTable; } + + /*! + * \brief The name of the field on the maintable this relation describes. + * \return The name of the field on the maintable this relation describes. + */ + const QString& indexColumn() const { return m_indexColumn; } + + /*! + * \brief Set the name of the field on the maintable this relation describes. + * \param _indexColumn - The name of the field on the maintable this relation describes. + */ + void setIndexColumn( const QString &_indexColumn ) { m_indexColumn = _indexColumn; } + + /*! + * \brief Gets the name of the primary key field of the related table. + * \return The name of the primary key field of the related table. + */ + const QString& foreignPrimKey() const { return m_foreignPrimKey; } + + /*! + * \brief Sets the name of the primary key field of the related table. + * \param _foreignPrimKey - The name of the primary key field of the related table. + */ + void setForeignPrimKey( const QString &_foreignPrimKey ) { m_foreignPrimKey = _foreignPrimKey; } + + /*! + * \brief Return the relation structure as a QString for Debugging purposes + * \return The datastructure in QString representation. + */ + QString asString() const { return QString( "SchemaName : %1 \nTableName : %2 \n ConstraintName : %3 \n ForeignTable : %4 \n IndexColumn : %5 \n Foreign Primary Key : %6") + .arg( m_schemaName, m_tableName, m_constraintName, m_foreignTable, m_indexColumn, m_foreignPrimKey ); } + +private: + QString m_schemaName; ///< The name of the database scheme + QString m_tableName; ///< The name of the target-table + QString m_constraintName; ///< The name of the constraint as known to the database + QString m_foreignTable; ///< The name of the table the target has a relation with + QString m_indexColumn; ///< The name of the column of the target table that holds the relation. + QString m_foreignPrimKey; ///< The name of the primary key-field of the related table. +}; + +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_DBRELATION_H */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..3635320 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.0) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) + +include(projectheader) +project_header(test_logutils) + +include_directories( SYSTEM + ${CMAKE_CURRENT_SOURCE_DIR}/../../src +) + +include(compiler) +set(SRC_LIST +) + +# add_executable( ${PROJECT_NAME} +# ${SRC_LIST} +# ) + +# target_link_libraries( +# ${PROJECT_NAME} +# ) + +# set_target_properties( ${PROJECT_NAME} PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +# LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib +# ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/archive +# ) + +# include(installation) +# install_application() -- libgit2 0.21.4