adatabase.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /*
  2. *openPilot Log - A FOSS Pilot Logbook Application
  3. *Copyright (C) 2020 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. #include "adatabase.h"
  19. #include "src/database/tablecolumnliterals.h"
  20. namespace experimental {
  21. ADatabaseError::ADatabaseError(QString msg_)
  22. : QSqlError::QSqlError(msg_)
  23. {}
  24. QString ADatabaseError::text() const
  25. {
  26. return "Database Error: " + QSqlError::text();
  27. }
  28. ADatabase* ADatabase::instance = nullptr;
  29. ADatabase* ADatabase::getInstance()
  30. {
  31. if(!instance)
  32. instance = new ADatabase();
  33. return instance;
  34. }
  35. bool ADatabase::connect()
  36. {
  37. const QString driver("QSQLITE");
  38. if (!QSqlDatabase::isDriverAvailable(driver))
  39. return false;
  40. QDir directory("data");
  41. QString databaseLocation = directory.filePath("logbook.db");
  42. QSqlDatabase db = QSqlDatabase::addDatabase(driver);
  43. db.setDatabaseName(databaseLocation);
  44. if (!db.open())
  45. return false;
  46. DEB("Database connection established.");
  47. // Enable foreign key restrictions
  48. QSqlQuery query("PRAGMA foreign_keys = ON;");
  49. tableNames = db.tables();
  50. QStringList column_names;
  51. for (const auto &table : tableNames) {
  52. column_names.clear();
  53. QSqlRecord fields = db.record(table);
  54. for (int i = 0; i < fields.count(); i++) {
  55. column_names.append(fields.field(i).name());
  56. tableColumns.insert(table, column_names);
  57. }
  58. }
  59. DEB("Database Tables: " << tableNames);
  60. DEB("Tables and Columns: " << tableColumns);
  61. return true;
  62. }
  63. void ADatabase::disconnect()
  64. {
  65. auto db = ADatabase::database();
  66. db.close();
  67. db.removeDatabase(db.connectionName());
  68. DEB("Database connection closed.");
  69. }
  70. QSqlDatabase ADatabase::database()
  71. {
  72. return QSqlDatabase::database("qt_sql_default_connection");
  73. }
  74. bool ADatabase::commit(AEntry entry)
  75. {
  76. if (exists(entry)) {
  77. return update(entry);
  78. } else {
  79. return insert(entry);
  80. }
  81. }
  82. bool ADatabase::remove(AEntry entry)
  83. {
  84. if (!exists(entry)) {
  85. DEB("Error: Database entry not found.");
  86. lastError = ADatabaseError("Database entry not found.");
  87. return false;
  88. }
  89. QString statement = "DELETE FROM " + entry.getPosition().tableName +
  90. " WHERE ROWID?";
  91. QSqlQuery query;
  92. query.prepare(statement);
  93. query.addBindValue(entry.getPosition().rowId);
  94. query.exec();
  95. if (query.lastError().type() == QSqlError::NoError)
  96. {
  97. DEB("Entry " << entry.getPosition().tableName << entry.getPosition().rowId << " removed.");
  98. emit dataBaseUpdated();
  99. lastError = QString();
  100. return true;
  101. } else {
  102. DEB("Unable to delete.");
  103. DEB("Query: " << statement);
  104. DEB("Query Error: " << query.lastError().text());
  105. lastError = query.lastError().text();
  106. return false;
  107. }
  108. }
  109. bool ADatabase::removeMany(QList<DataPosition> data_position_list)
  110. {
  111. int errorCount = 0;
  112. QSqlQuery query;
  113. query.prepare("BEGIN EXCLUSIVE TRANSACTION");
  114. query.exec();
  115. for (const auto& data_position : data_position_list) {
  116. if (!exists(data_position)) {
  117. lastError = ADatabaseError("Database entry not found.");
  118. errorCount++;
  119. }
  120. QString statement = "DELETE FROM " + data_position.first +
  121. " WHERE ROWID=" + QString::number(data_position.second);
  122. query.prepare(statement);
  123. query.exec();
  124. if (!(query.lastError().type() == QSqlError::NoError))
  125. errorCount++;
  126. }
  127. if (errorCount == 0) {
  128. query.prepare("COMMIT");
  129. query.exec();
  130. if(query.lastError().type() == QSqlError::NoError) {
  131. emit dataBaseUpdated();
  132. lastError = QString();
  133. return true;
  134. } else {
  135. lastError = "Transaction unsuccessful (Interrupted). Error count: " + QString::number(errorCount);
  136. return false;
  137. }
  138. } else {
  139. query.prepare("ROLLBACK");
  140. query.exec();
  141. lastError = "Transaction unsuccessful (no changes have been made). Error count: " + QString::number(errorCount);
  142. return false;
  143. }
  144. }
  145. bool ADatabase::exists(AEntry entry)
  146. {
  147. if(entry.getPosition().second == 0)
  148. return false;
  149. //Check database for row id
  150. QString statement = "SELECT COUNT(*) FROM " + entry.getPosition().tableName +
  151. " WHERE ROWID=?";
  152. QSqlQuery query;
  153. query.prepare(statement);
  154. query.addBindValue(entry.getPosition().rowId);
  155. query.setForwardOnly(true);
  156. query.exec();
  157. //this returns either 1 or 0 since row ids are unique
  158. if (!query.isActive()) {
  159. lastError = query.lastError().text();
  160. DEB("Query Error: " << query.lastError().text() << statement);
  161. return false;
  162. }
  163. query.next();
  164. int rowId = query.value(0).toInt();
  165. if (rowId) {
  166. return true;
  167. } else {
  168. DEB("Database entry not found.");
  169. lastError = ADatabaseError("Database entry not found.");
  170. return false;
  171. }
  172. }
  173. bool ADatabase::exists(DataPosition data_position)
  174. {
  175. if(data_position.second == 0)
  176. return false;
  177. //Check database for row id
  178. QString statement = "SELECT COUNT(*) FROM " + data_position.first +
  179. " WHERE ROWID=?";
  180. QSqlQuery query;
  181. query.prepare(statement);
  182. query.addBindValue(data_position.second);
  183. query.setForwardOnly(true);
  184. query.exec();
  185. //this returns either 1 or 0 since row ids are unique
  186. if (!query.isActive()) {
  187. lastError = query.lastError().text();
  188. DEB("Query Error: " << query.lastError().text() << statement);
  189. }
  190. query.next();
  191. int rowId = query.value(0).toInt();
  192. if (rowId) {
  193. return true;
  194. } else {
  195. DEB("No entry exists at DataPosition: " << data_position);
  196. lastError = ADatabaseError("Database entry not found.");
  197. return false;
  198. }
  199. }
  200. bool ADatabase::update(AEntry updated_entry)
  201. {
  202. auto data = updated_entry.getData();
  203. QString statement = "UPDATE " + updated_entry.getPosition().tableName + " SET ";
  204. for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
  205. statement.append(i.key() + "=?,");
  206. }
  207. statement.chop(1);
  208. statement.append(" WHERE ROWID=?");
  209. QSqlQuery query;
  210. query.prepare(statement);
  211. for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
  212. if (i.value() == QVariant(QVariant::String)) {
  213. query.addBindValue(QVariant(QVariant::String));
  214. } else {
  215. query.addBindValue(i.value());
  216. }
  217. }
  218. query.addBindValue(updated_entry.getPosition().rowId);
  219. query.exec();
  220. if (query.lastError().type() == QSqlError::NoError)
  221. {
  222. DEB("Entry successfully committed.");
  223. emit dataBaseUpdated();
  224. lastError = QString();
  225. return true;
  226. } else {
  227. DEB("Unable to commit.");
  228. DEB("Query: " << statement);
  229. DEB("Query Error: " << query.lastError().text());
  230. lastError = query.lastError().text();
  231. return false;
  232. }
  233. }
  234. bool ADatabase::insert(AEntry new_entry)
  235. {
  236. auto data = new_entry.getData();
  237. QString statement = "INSERT INTO " + new_entry.getPosition().tableName + QLatin1String(" (");
  238. QMap<QString, QVariant>::iterator i;
  239. for (i = data.begin(); i != data.end(); ++i) {
  240. statement.append(i.key() + ',');
  241. }
  242. statement.chop(1);
  243. statement += QLatin1String(") VALUES (");
  244. for (int i=0; i < data.size(); ++i) {
  245. statement += QLatin1String("?,");
  246. }
  247. statement.chop(1);
  248. statement += ')';
  249. QSqlQuery query;
  250. query.prepare(statement);
  251. for (i = data.begin(); i != data.end(); ++i) {
  252. if (i.value() == "") {
  253. query.addBindValue(QVariant(QVariant::String));
  254. } else {
  255. query.addBindValue(i.value());
  256. }
  257. }
  258. query.exec();
  259. //check result.
  260. if (query.lastError().type() == QSqlError::NoError)
  261. {
  262. DEB("Entry successfully committed.");
  263. emit dataBaseUpdated();
  264. lastError = QString();
  265. return true;
  266. } else {
  267. DEB("Unable to commit.");
  268. DEB("Query: " << statement);
  269. DEB("Query Error: " << query.lastError().text());
  270. lastError = query.lastError().text();
  271. return false;
  272. }
  273. }
  274. TableData ADatabase::getEntryData(DataPosition data_position)
  275. {
  276. // check table exists
  277. if (!tableNames.contains(data_position.first)) {
  278. DEB(data_position.first << " not a table in the database. Unable to retreive Entry data.");
  279. return TableData();
  280. }
  281. //Check Database for rowId
  282. QString statement = "SELECT COUNT(*) FROM " + data_position.first
  283. + " WHERE ROWID=?";
  284. QSqlQuery check_query;
  285. check_query.prepare(statement);
  286. check_query.addBindValue(data_position.second);
  287. check_query.setForwardOnly(true);
  288. check_query.exec();
  289. if (check_query.lastError().type() != QSqlError::NoError) {
  290. DEB("SQL error: " << check_query.lastError().text());
  291. DEB("Statement: " << statement);
  292. lastError = check_query.lastError().text();
  293. return TableData();
  294. }
  295. check_query.next();
  296. if (check_query.value(0).toInt() == 0) {
  297. DEB("No Entry found for row id: " << data_position.second );
  298. lastError = ADatabaseError("Database entry not found.");
  299. return TableData();
  300. }
  301. // Retreive TableData
  302. statement = "SELECT * FROM " + data_position.first
  303. + " WHERE ROWID=?";
  304. QSqlQuery select_query;
  305. select_query.prepare(statement);
  306. select_query.addBindValue(data_position.second);
  307. select_query.setForwardOnly(true);
  308. select_query.exec();
  309. if (select_query.lastError().type() != QSqlError::NoError) {
  310. DEB("SQL error: " << select_query.lastError().text());
  311. DEB("Statement: " << statement);
  312. lastError = select_query.lastError().text();
  313. return TableData();
  314. }
  315. select_query.next();
  316. TableData entry_data;
  317. for (const auto &column : tableColumns.value(data_position.first)) {
  318. entry_data.insert(column, select_query.value(column));
  319. }
  320. return entry_data;
  321. }
  322. AEntry ADatabase::getEntry(DataPosition data_position)
  323. {
  324. AEntry entry(data_position);
  325. entry.setData(getEntryData(data_position));
  326. return entry;
  327. }
  328. APilotEntry ADatabase::getPilotEntry(RowId row_id)
  329. {
  330. APilotEntry pilot_entry(row_id);
  331. pilot_entry.setData(getEntryData(pilot_entry.getPosition()));
  332. return pilot_entry;
  333. }
  334. ATailEntry ADatabase::getTailEntry(RowId row_id)
  335. {
  336. ATailEntry tail_entry(row_id);
  337. tail_entry.setData(getEntryData(tail_entry.getPosition()));
  338. return tail_entry;
  339. }
  340. AAircraftEntry ADatabase::getAircraftEntry(RowId row_id)
  341. {
  342. AAircraftEntry aircraft_entry(row_id);
  343. aircraft_entry.setData(getEntryData(aircraft_entry.getPosition()));
  344. return aircraft_entry;
  345. }
  346. AFlightEntry ADatabase::getFlightEntry(RowId row_id)
  347. {
  348. AFlightEntry flight_entry(row_id);
  349. flight_entry.setData(getEntryData(flight_entry.getPosition()));
  350. return flight_entry;
  351. }
  352. const QStringList ADatabase::getCompletionList(ADatabaseTarget target)
  353. {
  354. QString statement;
  355. switch (target) {
  356. case ADatabaseTarget::pilots:
  357. statement.append("SELECT piclastname||', '||picfirstname FROM pilots");
  358. break;
  359. case ADatabaseTarget::aircraft:
  360. statement.append("SELECT make||' '||model FROM aircraft WHERE model IS NOT NULL "
  361. "UNION "
  362. "SELECT make||' '||model||'-'||variant FROM aircraft WHERE variant IS NOT NULL");
  363. break;
  364. case ADatabaseTarget::airport_identifier_all:
  365. statement.append("SELECT icao FROM airports UNION SELECT iata FROM airports");
  366. break;
  367. case ADatabaseTarget::registrations:
  368. statement.append("SELECT registration FROM tails");
  369. break;
  370. case ADatabaseTarget::companies:
  371. statement.append("SELECT company FROM pilots");
  372. break;
  373. default:
  374. DEB("Not a valid completer target for this function.");
  375. return QStringList();
  376. }
  377. QSqlQuery query;
  378. query.prepare(statement);
  379. query.setForwardOnly(true);
  380. query.exec();
  381. if(!query.isActive()) {
  382. lastError = query.lastError().text();
  383. return QStringList();
  384. }
  385. QStringList completer_list;
  386. while (query.next())
  387. completer_list.append(query.value(0).toString());
  388. completer_list.sort();
  389. completer_list.removeAll(QString(""));
  390. completer_list.removeDuplicates();
  391. return completer_list;
  392. }
  393. const QMap<QString, int> ADatabase::getIdMap(ADatabaseTarget target)
  394. {
  395. QString statement;
  396. switch (target) {
  397. case ADatabaseTarget::pilots:
  398. statement.append("SELECT ROWID, piclastname||', '||picfirstname FROM pilots");
  399. break;
  400. case ADatabaseTarget::aircraft:
  401. statement.append("SELECT ROWID, make||' '||model FROM aircraft WHERE model IS NOT NULL "
  402. "UNION "
  403. "SELECT ROWID, make||' '||model||'-'||variant FROM aircraft WHERE variant IS NOT NULL");
  404. break;
  405. case ADatabaseTarget::airport_identifier_icao:
  406. statement.append("SELECT ROWID, icao FROM airports");
  407. break;
  408. case ADatabaseTarget::airport_identifier_iata:
  409. statement.append("SELECT ROWID, iata FROM airports WHERE iata NOT NULL");
  410. break;
  411. case ADatabaseTarget::airport_names:
  412. statement.append("SELECT ROWID, name FROM airports");
  413. break;
  414. case ADatabaseTarget::tails:
  415. statement.append("SELECT ROWID, registration FROM tails");
  416. break;
  417. default:
  418. DEB("Not a valid completer target for this function.");
  419. return QMap<QString, int>();
  420. }
  421. auto id_map = QMap<QString, int>();
  422. auto query = QSqlQuery(statement);
  423. if (!query.isActive()) {
  424. DEB("No result found. Check Query and Error.");
  425. DEB("Query: " << statement);
  426. DEB("Error: " << query.lastError().text());
  427. lastError = query.lastError().text();
  428. return QMap<QString, int>();
  429. } else {
  430. QVector<QString> query_result;
  431. while (query.next()) {
  432. id_map.insert(query.value(1).toString(), query.value(0).toInt());
  433. }
  434. return id_map;
  435. }
  436. }
  437. int ADatabase::getLastEntry(ADatabaseTarget target)
  438. {
  439. QString statement = "SELECT MAX(ROWID) FROM ";
  440. switch (target) {
  441. case ADatabaseTarget::pilots:
  442. statement.append(DB_TABLE_PILOTS);
  443. break;
  444. case ADatabaseTarget::aircraft:
  445. statement.append(DB_TABLE_AIRCRAFT);
  446. break;
  447. case ADatabaseTarget::tails:
  448. statement.append(DB_TABLE_TAILS);
  449. break;
  450. default:
  451. DEB("Not a valid completer target for this function.");
  452. return 0;
  453. }
  454. auto query = QSqlQuery(statement);
  455. if (query.first()) {
  456. return query.value(0).toInt();
  457. } else {
  458. lastError = ADatabaseError("Database entry not found.");
  459. DEB("No entry found.");
  460. return 0;
  461. }
  462. }
  463. QVector<QString> ADatabase::customQuery(QString statement, int return_values)
  464. {
  465. QSqlQuery query(statement);
  466. query.exec();
  467. if (!query.first()) {
  468. DEB("No result found. Check Query and Error.");
  469. DEB("Error: " << query.lastError().text());
  470. DEB("Statement: " << statement);
  471. lastError = query.lastError().text();
  472. return QVector<QString>();
  473. } else {
  474. query.first();
  475. query.previous();
  476. QVector<QString> result;
  477. while (query.next()) {
  478. for (int i = 0; i < return_values ; i++) {
  479. result.append(query.value(i).toString());
  480. }
  481. }
  482. emit dataBaseUpdated();
  483. lastError = QString();
  484. return result;
  485. }
  486. }
  487. ADatabase* aDB() { return ADatabase::getInstance(); }
  488. }// namespace experimental