database.h 12 KB

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