/*
*openPilotLog - A FOSS Pilot Logbook Application
*Copyright (C) 2020-2021 Felix Turowsky
*
*This program is free software: you can redistribute it and/or modify
*it under the terms of the GNU General Public License as published by
*the Free Software Foundation, either version 3 of the License, or
*(at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program. If not, see .
*/
#ifndef DATABASE_H
#define DATABASE_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "src/opl.h"
#include "src/database/row.h"
#include "src/classes/astandardpaths.h"
using RowData_T = QHash;
namespace OPL {
/*!
* \brief Convenience macro that returns instance of DataBase.
* Instead of this:
* OPL::DataBase::getInstance().commit(...)
* Use this:
* DB->commit(...)
*/
#define DB OPL::Database::instance()
/*!
* \brief The UserDateState struct caches the current number of entries in relevant database tables
* for fast access
* \param numTails - Number of tails in the database
* \param numPilots - Number of pilots in the database
*/
struct UserDataState {
UserDataState(){numTails = 0; numPilots = 0;}
UserDataState(int numTails_, int numPilots_)
: numTails(numTails_), numPilots(numPilots_){}
bool operator==(const UserDataState& other)
{
return numTails == other.numTails && numPilots == other.numPilots;
}
bool operator!=(const UserDataState& other)
{
return numTails != other.numTails || numPilots != other.numPilots;
}
int numTails;
int numPilots;
};
/*!
* \brief The DB class encapsulates the SQL database by providing fast access
* to hot database data.
*/
class Database : public QObject {
private:
Q_OBJECT
Database()
: databaseFile(QFileInfo(AStandardPaths::directory(AStandardPaths::Database).
absoluteFilePath(QStringLiteral("logbook.db"))))
{}
static Database* self;
const QFileInfo databaseFile;
QStringList tableNames;
QHash tableColumns;
inline const static QString SQLITE_DRIVER = QStringLiteral("QSQLITE");
inline const static QList USER_TABLES = {
OPL::DbTable::Flights,
OPL::DbTable::Pilots,
OPL::DbTable::Tails
};
inline const static QList TEMPLATE_TABLES = {
OPL::DbTable::Aircraft,
OPL::DbTable::Airports,
OPL::DbTable::Currencies,
OPL::DbTable::Changelog
};
public:
Database(const Database&) = delete;
void operator=(const Database&) = delete;
static Database* instance();
/*!
* \brief Holds information about the last error that ocurred during
* a SQL operation. If the error type is QSqlError::UnknownError, the error is related to data
* from the database (entry not found,...), otherwise the error is related to SQL execution. In this
* case error.type() provides further information.
*
* If the error type is QSqlError::NoError, the last executed database query was successful.
*/
QSqlError lastError;
/*!
* \brief Connect to the database and populate database information.
*/
bool connect();
/*!
* \brief closes the database connection.
*/
void disconnect();
/*!
* \brief Updates the member variables tableNames and tableColumns with up-to-date layout information
* if the database has been altered. This function is normally only required during database setup or maintenance.
*/
void updateLayout();
/*!
* \brief Return the database revision number (not the sqlite version number).
*/
const QString version() const;
/*!
* \brief Database::sqliteVersion returns the database sqlite version. See also dbRevision()
* \return sqlite version string
*/
const QString sqliteVersion() const;
/*!
* \brief Return the names of all tables in the database
*/
const QStringList getTableNames() const;
/*!
* \brief Return the names of a given table in the database.
*/
const QStringList getTableColumns(OPL::DbTable table_name) const;
/*!
* \brief Can be used to access the database connection.
* \return The QSqlDatabase object pertaining to the connection.
*/
static QSqlDatabase database();
/*!
* \brief Can be used to send a complex query to the database.
* \param query - the full sql query statement
* \param returnValues - the number of return values
*/
QVector customQuery(QString statement, int return_values);
/*!
* \brief Checks if an entry exists in the database, based on position data
*/
bool exists(const OPL::Row &row);
/*!
* \brief clear resets the database, i.e. deletes all content in the tables containing
* userdata (pilots, flights, tails)
*/
bool clear();
/*!
* \brief commits an entry to the database, calls either insert or update,
* based on position data
*/
bool commit(const OPL::Row &row);
/*!
* \brief commits data imported from JSON
* \details This function is used to import values to the databases which are held in JSON documents.
* These entries are pre-filled data used for providing completion data, such as Airport or Aircraft Type Data.
*/
bool commit(const QJsonArray &json_arr, const OPL::DbTable table);
/*!
* \brief Create new entry in the databse based on UserInput
*/
bool insert(const OPL::Row &new_row);
/*!
* \brief Updates entry in database from existing entry tweaked by the user.
*/
bool update(const OPL::Row &updated_row);
/*!
* \brief deletes an entry from the database.
*/
bool remove(const OPL::Row &row);
/*!
* \brief deletes a batch of entries from the database. Optimised for speed when
* deleting many entries. The entries are identified using their row id
*/
bool removeMany(OPL::DbTable table, const QList &row_id_list);
/*!
* \brief retreive a Row from the database
*/
OPL::Row getRow(const OPL::DbTable table, const int row_id);
/*!
* \brief retreive a Map of for a specific row in the database.
*/
RowData_T getRowData(const OPL::DbTable table, const int row_id);
/*!
* \brief retreives a PilotEntry from the database. See row class for details.
*/
inline OPL::PilotEntry getPilotEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Pilots, row_id);
return OPL::PilotEntry(row_id, data);
}
/*!
* \brief retreives a TailEntry from the database. See row class for details.
*/
inline OPL::TailEntry getTailEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Tails, row_id);
return OPL::TailEntry(row_id, data);
}
/*!
* \brief retreives a TailEntry from the database. See row class for details.
*/
inline OPL::AircraftEntry getAircraftEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Aircraft, row_id);
return OPL::AircraftEntry(row_id, data);
}
/*!
* \brief retreives a flight entry from the database. See row class for details.
*/
inline OPL::FlightEntry getFlightEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Flights, row_id);
return OPL::FlightEntry(row_id, data);
}
/*!
* \brief retreives a Simulator entry from the database. See row class for details.
*/
inline OPL::SimulatorEntry getSimEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Simulators, row_id);
return OPL::SimulatorEntry(row_id, data);
}
/*!
* \brief Retreives a currency entry from the database. See row class for details.
*/
inline OPL::CurrencyEntry getCurrencyEntry(int row_id)
{
const auto data = getRowData(OPL::DbTable::Currencies, row_id);
return OPL::CurrencyEntry(row_id, data);
}
/*!
* \brief returns the ROWID for the newest entry in the respective table.
*/
int getLastEntry(OPL::DbTable table);
/*!
* \brief returns a list of ROWID's in the flights table for which foreign key constraints
* exist.
*/
QList getForeignKeyConstraints(int foreign_row_id, OPL::DbTable table);
/*!
* \brief getTable returns all contents of a given table from the database
* \return
*/
QVector getTable(OPL::DbTable table);
/*!
* \brief getUserTables returns a list of the of the tables that contain user-created data
* (flights, pilots,..)
*/
const QList &getUserTables() const;
/*!
* \brief getTemplateTables returns a list of the tables that contain template data
* (aiports, aircraft,..)
*/
const QList &getTemplateTables() const;
/*!
* \brief getUserDataState returns a struct containing the current amount of entries in the tails and
* pilots tables.
* \return
*/
const UserDataState getUserDataState() const;
// Maintenance and setup
/*!
* \brief Create or restore the database to its ready-to-use but empty state
* \details The SQL code for the database creation is stored in a .sql file which is available as a ressource.
* This file gets read, and the querys executed. If errors occur, returns false.
*/
bool createSchema();
/*!
* \brief importTemplateData fills an empty database with the template
* data (Aircraft, Airports, currencies, changelog) as read from the JSON
* templates.
* \param use_local_ressources determines whether the included ressource files
* or a previously downloaded file should be used.
* \return
*/
bool importTemplateData(bool use_local_ressources);
/*!
* \brief Delete all rows from the user data tables (flights, pliots, tails)
*/
bool resetUserData();
/*!
* \brief Database::createBackup copies the currently used database to an external backup location provided by the user
* \param dest_file This is the full path and filename of where the backup will be created, e.g. 'home/Sully/myBackups/backupFromOpl.db'
*/
bool createBackup(const QString& dest_file);
/*!
* \brief Database::restoreBackup restores the database from a given backup file and replaces the currently active database.
* \param backup_file This is the full path and filename of the backup, e.g. 'home/Sully/myBackups/backupFromOpl.db'
*/
bool restoreBackup(const QString& backup_file);
signals:
/*!
* \brief updated is emitted whenever the database contents have been updated.
* This can be either a commit, update or remove. This signal should be used to
* trigger an update to the models of the views displaying database contents in
* the user interface so that a user is always presented with up-to-date information.
*/
void dataBaseUpdated();
/*!
* \brief connectionReset is emitted whenever the database connection is reset, for
* example when creating or restoring a backup.
*/
void connectionReset();
};
} // namespace OPL
#endif // DATABASE_H