database.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /*
  2. *openPilotLog - A FOSS Pilot Logbook Application
  3. *Copyright (C) 2020-2023 Felix Turowsky
  4. *
  5. *This program is free software: you can redistribute it and/or modify
  6. *it under the terms of the GNU General Public License as published by
  7. *the Free Software Foundation, either version 3 of the License, or
  8. *(at your option) any later version.
  9. *
  10. *This program is distributed in the hope that it will be useful,
  11. *but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. *GNU General Public License for more details.
  14. *
  15. *You should have received a copy of the GNU General Public License
  16. *along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #ifndef DATABASE_H
  19. #define DATABASE_H
  20. #include <QPair>
  21. #include <QHash>
  22. #include <QString>
  23. #include <QDir>
  24. #include <QSqlDatabase>
  25. #include <QSqlDriver>
  26. #include <QSqlQuery>
  27. #include <QSqlError>
  28. #include <QSqlTableModel>
  29. #include <QSqlQuery>
  30. #include <QSqlRecord>
  31. #include <QSqlField>
  32. #include "src/classes/paths.h"
  33. #include "src/database/aircraftentry.h"
  34. #include "src/database/airportentry.h"
  35. #include "src/database/currencyentry.h"
  36. #include "src/database/flightentry.h"
  37. #include "src/database/pilotentry.h"
  38. #include "src/database/simulatorentry.h"
  39. #include "src/database/tailentry.h"
  40. #include "src/opl.h"
  41. #include "src/database/row.h"
  42. namespace OPL {
  43. /*!
  44. * \brief Convenience macro that returns instance of DataBase.
  45. * Instead of this:
  46. * OPL::DataBase::getInstance().commit(...)
  47. * Use this:
  48. * DB->commit(...)
  49. */
  50. #define DB OPL::Database::instance()
  51. /*!
  52. * \brief The DB class encapsulates the SQL database by providing fast access
  53. * to hot database data.
  54. */
  55. class Database : public QObject {
  56. private:
  57. Q_OBJECT
  58. Database()
  59. : databaseFile(OPL::Paths::databaseFileInfo())
  60. {}
  61. static Database* self;
  62. const QFileInfo databaseFile;
  63. QStringList tableNames;
  64. QHash<QString, QStringList> tableColumns;
  65. inline const static QString SQLITE_DRIVER = QStringLiteral("QSQLITE");
  66. inline const static QList<OPL::DbTable> USER_TABLES = {
  67. OPL::DbTable::Flights,
  68. OPL::DbTable::Pilots,
  69. OPL::DbTable::Tails
  70. };
  71. inline const static QList<OPL::DbTable> TEMPLATE_TABLES = {
  72. OPL::DbTable::Aircraft,
  73. OPL::DbTable::Airports,
  74. OPL::DbTable::Currencies,
  75. OPL::DbTable::Changelog,
  76. };
  77. public:
  78. Database(const Database&) = delete;
  79. void operator=(const Database&) = delete;
  80. static Database* instance();
  81. /*!
  82. * \brief Holds information about the last error that ocurred during
  83. * a SQL operation. If the error type is QSqlError::UnknownError, the error is related to data
  84. * from the database (entry not found,...), otherwise the error is related to SQL execution. In this
  85. * case error.type() provides further information.
  86. *
  87. * If the error type is QSqlError::NoError, the last executed database query was successful.
  88. */
  89. QSqlError lastError;
  90. /*!
  91. * \brief Connect to the database and populate database information.
  92. */
  93. bool connect();
  94. /*!
  95. * \brief closes the database connection.
  96. */
  97. void disconnect();
  98. /*!
  99. * \brief Updates the member variables tableNames and tableColumns with up-to-date layout information
  100. * if the database has been altered. This function is normally only required during database setup or maintenance.
  101. */
  102. void updateLayout();
  103. /*!
  104. * \brief Return the database revision number (not the sqlite version number).
  105. */
  106. const QString version() const;
  107. /*!
  108. * \brief Database::sqliteVersion returns the database sqlite version. See also dbRevision()
  109. * \return sqlite version string
  110. */
  111. const QString sqliteVersion() const;
  112. /*!
  113. * \brief Return the names of all tables in the database
  114. */
  115. const QStringList getTableNames() const;
  116. /*!
  117. * \brief Return the names of a given table in the database.
  118. */
  119. const QStringList getTableColumns(OPL::DbTable table_name) const;
  120. /*!
  121. * \brief Can be used to access the database connection.
  122. * \return The QSqlDatabase object pertaining to the connection.
  123. */
  124. static QSqlDatabase database();
  125. /*!
  126. * \brief Can be used to send a complex query to the database.
  127. * \param query - the full sql query statement
  128. * \param returnValues - the number of return values
  129. */
  130. QVector<QVariant> customQuery(QString statement, int return_values);
  131. /*!
  132. * \brief Checks if an entry exists in the database, based on position data
  133. */
  134. bool exists(const OPL::Row &row);
  135. /*!
  136. * \brief clear resets the database, i.e. deletes all content in the tables containing
  137. * userdata (pilots, flights, tails)
  138. */
  139. bool clear();
  140. /*!
  141. * \brief commits an entry to the database, calls either insert or update,
  142. * based on position data
  143. */
  144. bool commit(const OPL::Row &row);
  145. /*!
  146. * \brief commits data imported from JSON
  147. * \details This function is used to import values to the databases which are held in JSON documents.
  148. * These entries are pre-filled data used for providing completion data, such as Airport or Aircraft Type Data.
  149. */
  150. bool commit(const QJsonArray &json_arr, const OPL::DbTable table);
  151. /*!
  152. * \brief Create new entry in the databse based on UserInput
  153. */
  154. bool insert(const OPL::Row &new_row);
  155. /*!
  156. * \brief Updates entry in database from existing entry tweaked by the user.
  157. */
  158. bool update(const OPL::Row &updated_row);
  159. /*!
  160. * \brief deletes an entry from the database.
  161. */
  162. bool remove(const OPL::Row &row);
  163. /*!
  164. * \brief deletes a batch of entries from the database. Optimised for speed when
  165. * deleting many entries. The entries are identified using their row id
  166. */
  167. bool removeMany(OPL::DbTable table, const QList<int> &row_id_list);
  168. /*!
  169. * \brief retreive a Row from the database
  170. */
  171. OPL::Row getRow(const OPL::DbTable table, const int row_id);
  172. /*!
  173. * \brief retreive a Map of <column name, column content> for a specific row in the database.
  174. */
  175. RowData_T getRowData(const OPL::DbTable table, const int row_id);
  176. /*!
  177. * \brief retreives a PilotEntry from the database. See row class for details.
  178. */
  179. inline OPL::PilotEntry getPilotEntry(int row_id)
  180. {
  181. const auto data = getRowData(OPL::DbTable::Pilots, row_id);
  182. return OPL::PilotEntry(row_id, data);
  183. }
  184. /*!
  185. * \brief get the database entry for the logbook owner (self)
  186. */
  187. inline OPL::PilotEntry getLogbookOwner()
  188. {
  189. auto data = getRowData(OPL::DbTable::Pilots, 1);
  190. data.insert(OPL::PilotEntry::ROWID, 1);
  191. return OPL::PilotEntry(1, data);
  192. }
  193. /*!
  194. * \brief Set the database entry for the logbook owner (self)
  195. */
  196. inline bool setLogbookOwner(RowData_T &ownerData)
  197. {
  198. if(ownerData.value(OPL::PilotEntry::LASTNAME).isNull()) {
  199. lastError = QSqlError("Logbook owners last name is mandatory.");
  200. return false;
  201. }
  202. ownerData.insert(OPL::PilotEntry::ROWID, 1);
  203. return commit(OPL::PilotEntry(1, ownerData));
  204. }
  205. /*!
  206. * \brief retreives a TailEntry from the database. See row class for details.
  207. */
  208. inline OPL::TailEntry getTailEntry(int row_id)
  209. {
  210. const auto data = getRowData(OPL::DbTable::Tails, row_id);
  211. return OPL::TailEntry(row_id, data);
  212. }
  213. /*!
  214. * \brief retreives a TailEntry from the database. See row class for details.
  215. */
  216. inline OPL::AircraftEntry getAircraftEntry(int row_id)
  217. {
  218. const auto data = getRowData(OPL::DbTable::Aircraft, row_id);
  219. return OPL::AircraftEntry(row_id, data);
  220. }
  221. /*!
  222. * \brief retreives a flight entry from the database. See row class for details.
  223. */
  224. inline OPL::FlightEntry getFlightEntry(int row_id)
  225. {
  226. const auto data = getRowData(OPL::DbTable::Flights, row_id);
  227. return OPL::FlightEntry(row_id, data);
  228. }
  229. /*!
  230. * \brief retreives a Simulator entry from the database. See row class for details.
  231. */
  232. inline OPL::SimulatorEntry getSimEntry(int row_id)
  233. {
  234. const auto data = getRowData(OPL::DbTable::Simulators, row_id);
  235. return OPL::SimulatorEntry(row_id, data);
  236. }
  237. /*!
  238. * \brief Retreives a currency entry from the database. See row class for details.
  239. */
  240. inline OPL::CurrencyEntry getCurrencyEntry(int row_id)
  241. {
  242. const auto data = getRowData(OPL::DbTable::Currencies, row_id);
  243. return OPL::CurrencyEntry(row_id, data);
  244. }
  245. /*!
  246. * \brief Retreives an airport entry from the database. See row class for details.
  247. */
  248. inline OPL::AirportEntry getAirportEntry(int row_id)
  249. {
  250. const auto data = getRowData(OPL::DbTable::Airports, row_id);
  251. return OPL::AirportEntry(row_id, data);
  252. }
  253. /*!
  254. * \brief returns the ROWID for the newest entry in the respective table.
  255. */
  256. int getLastEntry(OPL::DbTable table);
  257. /*!
  258. * \brief returns a list of ROWID's in the flights table for which foreign key constraints
  259. * exist.
  260. */
  261. QList<int> getForeignKeyConstraints(int foreign_row_id, OPL::DbTable table);
  262. /*!
  263. * \brief getTable returns all contents of a given table from the database
  264. * \return
  265. */
  266. QVector<RowData_T> getTable(OPL::DbTable table);
  267. /*!
  268. * \brief getUserTables returns a list of the of the tables that contain user-created data
  269. * (flights, pilots,..)
  270. */
  271. const QList<OPL::DbTable> &getUserTables() const;
  272. /*!
  273. * \brief getTemplateTables returns a list of the tables that contain template data
  274. * (aiports, aircraft,..)
  275. */
  276. const QList<OPL::DbTable> &getTemplateTables() const;
  277. // Maintenance and setup
  278. /*!
  279. * \brief Create or restore the database to its ready-to-use but empty state
  280. * \details The SQL code for the database creation is stored in a .sql file which is available as a ressource.
  281. * This file gets read, and the querys executed. If errors occur, returns false.
  282. */
  283. bool createSchema();
  284. /*!
  285. * \brief importTemplateData fills an empty database with the template
  286. * data (Aircraft, Airports, currencies, changelog) as read from the JSON
  287. * templates.
  288. * \param use_local_ressources determines whether the included ressource files
  289. * or a previously downloaded file should be used.
  290. * \return
  291. */
  292. bool importTemplateData(bool use_local_ressources);
  293. /*!
  294. * \brief Delete all rows from the user data tables (flights, pliots, tails)
  295. */
  296. bool resetUserData();
  297. /*!
  298. * \brief Database::createBackup copies the currently used database to an external backup location provided by the user
  299. * \param dest_file This is the full path and filename of where the backup will be created, e.g. 'home/Sully/myBackups/backupFromOpl.db'
  300. */
  301. bool createBackup(const QString& dest_file);
  302. /*!
  303. * \brief Database::restoreBackup restores the database from a given backup file and replaces the currently active database.
  304. * \param backup_file This is the full path and filename of the backup, e.g. 'home/Sully/myBackups/backupFromOpl.db'
  305. */
  306. bool restoreBackup(const QString& backup_file);
  307. /*!
  308. * @brief Retreive the total time of all flight entries in the databas
  309. * @param includePreviousExperience determines whether experience from previous logbooks
  310. * is included.
  311. * @return The sum of all entries in the flights table
  312. */
  313. const RowData_T getTotals(bool includePreviousExperience);
  314. signals:
  315. /*!
  316. * \brief updated is emitted whenever the database contents have been updated.
  317. * This can be either a commit, update or remove. This signal should be used to
  318. * trigger an update to the models of the views displaying database contents in
  319. * the user interface so that a user is always presented with up-to-date information.
  320. */
  321. void dataBaseUpdated(const OPL::DbTable table);
  322. /*!
  323. * \brief connectionReset is emitted whenever the database connection is reset, for
  324. * example when creating or restoring a backup.
  325. */
  326. void connectionReset();
  327. };
  328. } // namespace OPL
  329. #endif // DATABASE_H