Browse Source

Initial Rework of Database Interface

Working on implementing a new Airport Class, some limitations of the current Database Interface became apparent. In order to make the interface easier to use, more performant and easier to scale, this commit is the first step of a DB Interface overhaul.

Included new classes:
Row - base class for a row (entry) in the database
TailEntry : Row
AircraftEntry : Row
Felix Turo 2 years ago
parent
commit
3f4c79fb05

+ 6 - 0
CMakeLists.txt

@@ -78,6 +78,12 @@ set(PROJECT_SOURCES
 
 
     # Classes
+    src/classes/row.h
+    src/classes/row.cpp
+    src/classes/tailentry.h
+    src/classes/tailentry.cpp
+    src/classes/aircraftentry.h
+    src/classes/aircraftentry.cpp
     src/classes/acompletiondata.h
     src/classes/acompletiondata.cpp
     src/classes/acurrencyentry.h

+ 35 - 4
mainwindow.cpp

@@ -25,14 +25,45 @@
 #include "src/gui/dialogues/newflightdialog.h"
 #include "src/functions/adatetime.h"
 #include "src/classes/aentry.h"
-
+#include "src/classes/row.h"
 #include "src/gui/dialogues/newsimdialog.h"
 // Quick and dirty Debug area
 void MainWindow::doDebugStuff()
 {
-    auto widget = new AirportWidget(this);
-    ui->stackedWidget->addWidget(widget);
-    ui->stackedWidget->setCurrentWidget(widget);
+    //auto widget = new AirportWidget(this);
+    //ui->stackedWidget->addWidget(widget);
+    //ui->stackedWidget->setCurrentWidget(widget);
+    //DEB << "Testing retreiving...";
+    //DEB << "Old";
+    //{
+    //ATimer timer;
+    //
+    //for (int i = 1; i < 1000; i++) {
+    //    DataPosition dp("airports", i);
+    //    auto entry = aDB->getEntry(dp);
+    //    //auto entry = aDB->(i);
+    //}
+    //DEB << "getEntry completed";
+    //}
+    //
+    //{
+    //ATimer timer;
+    //
+    //for (int i = 1; i < 1000; i++) {
+    //    auto row = aDB->getRow(OPL::DbTable::Airports, i);
+    //}
+    //DEB << "getRow completed";
+    //}
+    //OPL::Row new_row(OPL::DbTable::Aircraft, 4, entry.getData());
+    //DEB << new_row;
+    //DEB << aDB->commit(new_row);
+    //DEB << aDB->getAircraftEntry(4);
+
+    auto row = aDB->getRow(OPL::DbTable::Flights, 2);
+    DEB << row;
+    auto row_2 = aDB->getRow(OPL::DbTable::Flights, 500);
+    DEB << row_2;
+
 }
 
 MainWindow::MainWindow(QWidget *parent)

+ 5 - 5
src/classes/aflightentry.cpp

@@ -51,11 +51,11 @@ const QString AFlightEntry::summary()
     return flight_summary;
 }
 
-const QString AFlightEntry::getRegistration()
-{
-    ATailEntry acft = aDB->resolveForeignTail(tableData.value(OPL::Db::FLIGHTS_ACFT).toInt());
-    return acft.registration();
-}
+//const QString AFlightEntry::getRegistration()
+//{
+//    ATailEntry acft = aDB->resolveForeignTail(tableData.value(OPL::Db::FLIGHTS_ACFT).toInt());
+//    return acft.registration();
+//}
 
 const QString AFlightEntry::getPilotName(pilotPosition pilot_)
 {

+ 1 - 1
src/classes/aflightentry.h

@@ -40,7 +40,7 @@ public:
     /*!
      * \brief Returns the tails' registration from the database.
      */
-    const QString getRegistration();
+    //const QString getRegistration();
     /*!
      * \brief Returns the pilots name from the Database
      *

+ 17 - 0
src/classes/aircraftentry.cpp

@@ -0,0 +1,17 @@
+#include "aircraftentry.h"
+
+namespace OPL {
+
+AircraftEntry::AircraftEntry()
+    : Row(DbTable::Aircraft, 0)
+{}
+
+OPL::AircraftEntry::AircraftEntry(const RowData_T &row_data)
+    : Row(DbTable::Aircraft, 0, row_data)
+{}
+
+OPL::AircraftEntry::AircraftEntry(int row_id, const RowData_T &row_data)
+    : Row(DbTable::Aircraft, row_id, row_data)
+{}
+
+} // namespace OPL

+ 19 - 0
src/classes/aircraftentry.h

@@ -0,0 +1,19 @@
+#ifndef AIRCRAFTENTRY_H
+#define AIRCRAFTENTRY_H
+
+#include "row.h"
+
+namespace OPL {
+
+class AircraftEntry : public Row
+{
+public:
+    AircraftEntry();
+    AircraftEntry(const RowData_T &row_data);
+    AircraftEntry(int row_id, const RowData_T &row_data);
+};
+
+} // namespace OPL
+
+
+#endif // AIRCRAFTENTRY_H

+ 96 - 0
src/classes/row.cpp

@@ -0,0 +1,96 @@
+#include "row.h"
+
+namespace OPL {
+
+Row::Row(OPL::DbTable table_name, int row_id)
+    : table(table_name), rowId(row_id)
+{
+    hasData = false;
+}
+
+Row::Row(DbTable table_name)
+    : table(table_name)
+{
+    hasData = false;
+    rowId = 0; // new entry
+};
+
+Row::Row(OPL::DbTable table_name, int row_id, const RowData_T &row_data)
+    : table(table_name), rowId(row_id), rowData(row_data)
+{
+    hasData = true;
+};
+
+RowData_T Row::getRowData() const
+{
+    return rowData;
+}
+
+void Row::setRowData(const RowData_T &value)
+{
+    rowData = value;
+    hasData = true;
+}
+
+int Row::getRowId() const
+{
+    return rowId;
+}
+
+void Row::setRowId(int value)
+{
+    rowId = value;
+}
+
+OPL::DbTable Row::getTable() const
+{
+    return table;
+}
+
+OPL::Row::operator QString() const
+{
+    if (!isValid()) {
+        return QStringLiteral("Invalid Row");
+    }
+    QString out("\033[32m[Entry Data]:\t\033[m\n");
+    int item_count = 0;
+    QHash<ColName_T, ColData_T>::const_iterator i;
+    for (i = rowData.constBegin(); i!= rowData.constEnd(); ++i) {
+        QString spacer(":");
+        int spaces = (14 - i.key().length());
+        if (spaces > 0)
+            for (int i = 0; i < spaces ; i++)
+                spacer += QLatin1Char(' ');
+        if (i.value().toString().isEmpty()) {
+            out.append(QLatin1String("\t\033[m") + i.key()
+                       + spacer
+                       + QLatin1String("\033[35m----"));
+            spaces = (14 - i.value().toString().length());
+            spacer = QString();
+            if (spaces > 0)
+                for (int i = 0; i < spaces ; i++)
+                    spacer += QLatin1Char(' ');
+            out.append(spacer);
+        } else {
+            out.append(QLatin1String("\t\033[m") + i.key()
+                       + spacer
+                       + QLatin1String("\033[35m")
+                       + i.value().toString());
+
+            spaces = (14 - i.value().toString().length());
+            spacer = QString();
+            if (spaces > 0)
+                for (int i = 0; i < spaces ; i++)
+                    spacer += QLatin1Char(' ');
+            out.append(spacer);
+        }
+        item_count ++;
+        if (item_count % 4 == 0)
+            out.append(QLatin1String("\n"));
+    }
+    out.append(QLatin1String("\n"));
+    QTextStream(stdout) << out;
+    return QString();
+}
+
+} // namespace OPL

+ 43 - 0
src/classes/row.h

@@ -0,0 +1,43 @@
+#ifndef ROW_H
+#define ROW_H
+#include "src/opl.h"
+
+namespace OPL {
+
+/*!
+ * \brief The Row class provides an interface for retreiving and submitting entries from the database.
+ * It is a bass class and when instantiated, the appropriate subclass should be used.
+ */
+class Row
+{
+public:
+    Row() { valid = false;} // Require specifying position
+    Row(OPL::DbTable table_name, int row_id, const RowData_T &row_data);
+    Row(OPL::DbTable table_name, int row_id);
+    Row(OPL::DbTable table_name);
+
+    Row(const Row&) = default;
+    Row& operator=(const Row&) = default;
+
+    RowData_T getRowData() const;
+    void setRowData(const RowData_T &value);
+    int getRowId() const;
+    void setRowId(int value);
+    OPL::DbTable getTable() const;
+
+    bool isValid() const {return hasData && valid;}
+
+    // For Debugging
+    operator QString() const;
+
+
+private:
+    OPL::DbTable table;
+    int rowId;
+    RowData_T rowData;
+    bool hasData;
+    bool valid = true;
+};
+
+} // namespace OPL
+#endif // ROW_H

+ 37 - 0
src/classes/tailentry.cpp

@@ -0,0 +1,37 @@
+#include "tailentry.h"
+
+namespace OPL {
+
+TailEntry::TailEntry()
+    : Row(DbTable::Tails, 0)
+{}
+
+TailEntry::TailEntry(const RowData_T &row_data)
+    : Row(DbTable::Tails, 0, row_data)
+{}
+
+TailEntry::TailEntry(int row_id, const RowData_T &row_data)
+    : Row(DbTable::Tails, row_id, row_data)
+{}
+
+const QString TailEntry::registration() const
+{
+    return getRowData().value(OPL::Db::TAILS_REGISTRATION).toString();
+}
+
+const QString TailEntry::type() const
+{
+    QString type_string;
+    if (!getRowData().value(OPL::Db::TAILS_MAKE).toString().isEmpty())
+        type_string.append(getRowData().value(OPL::Db::TAILS_MAKE).toString() + QLatin1Char(' '));
+    if (!getRowData().value(OPL::Db::TAILS_MODEL).toString().isEmpty())
+        type_string.append(getRowData().value(OPL::Db::TAILS_MODEL).toString());
+    if (!getRowData().value(OPL::Db::TAILS_VARIANT).toString().isEmpty())
+        type_string.append(QLatin1Char('-') + getRowData().value(OPL::Db::TAILS_VARIANT).toString());
+
+    return type_string;
+}
+
+} // namespace OPL
+
+

+ 23 - 0
src/classes/tailentry.h

@@ -0,0 +1,23 @@
+#ifndef TAILENTRY_H
+#define TAILENTRY_H
+
+#include "row.h"
+
+namespace OPL {
+
+class TailEntry : public Row
+{
+public:
+    TailEntry();
+    TailEntry(const RowData_T &row_data); // create a new entry from scratch
+    TailEntry(int row_id, const RowData_T &row_data); // create an existing entry (retreived from DB)
+
+    const QString registration() const;
+    const QString type() const;
+};
+
+}
+
+
+
+#endif // TAILENTRY_H

+ 231 - 24
src/database/adatabase.cpp

@@ -264,6 +264,17 @@ bool ADatabase::commit(const AEntry &entry)
     }
 }
 
+bool ADatabase::commit(const OPL::Row &row)
+{
+    if (!row.isValid())
+        return false;
+
+    if (exists(row))
+        return update(row);
+    else
+        return insert(row);
+}
+
 bool ADatabase::commit(const QJsonArray &json_arr, const QString &table_name)
 {
     // create statement
@@ -329,6 +340,35 @@ bool ADatabase::remove(const AEntry &entry)
     }
 }
 
+bool ADatabase::remove(const OPL::Row &row)
+{
+    if (!exists(row)) {
+        LOG << "Error: Database entry not found.";
+        return false;
+    }
+
+    QString statement = QLatin1String("DELETE FROM ") + OPL::GLOBALS->getDbTableName(row.getTable())
+            + QLatin1String(" WHERE ROWID=?");
+
+    QSqlQuery query;
+    query.prepare(statement);
+    query.addBindValue(row.getRowId());
+
+    if (query.exec())
+    {
+        LOG << "Entry removed:";
+        LOG << row;
+        emit dataBaseUpdated();
+        return true;
+    } else {
+        DEB << "Unable to delete.";
+        DEB << "Query: " << statement;
+        DEB << "Query Error: " << query.lastError().text();
+        lastError = query.lastError();
+        return false;
+    }
+}
+
 bool ADatabase::removeMany(const QList<DataPosition> &data_position_list)
 {
     int errorCount = 0;
@@ -429,6 +469,35 @@ bool ADatabase::exists(DataPosition data_position)
     }
 }
 
+bool ADatabase::exists(const OPL::Row &row)
+{
+    if (row.getRowId() == 0)
+        return false;
+
+    //Check database for row id
+    QString statement = QLatin1String("SELECT COUNT(*) FROM ") + OPL::GLOBALS->getDbTableName(row.getTable())
+            + QLatin1String(" WHERE ROWID=?");
+    QSqlQuery query;
+    query.prepare(statement);
+    query.addBindValue(row.getRowId());
+    query.setForwardOnly(true);
+    query.exec();
+    //this returns either 1 or 0 since row ids are unique
+    if (!query.isActive()) {
+        lastError = query.lastError();
+        DEB << "Query Error: " << query.lastError().text() << statement;
+        return false;
+    }
+    query.next();
+    int rowId = query.value(0).toInt();
+    if (rowId) {
+        return true;
+    } else {
+        LOG << "Database entry not found.";
+        return false;
+    }
+}
+
 bool ADatabase::clear()
 {
     QSqlQuery q;
@@ -449,14 +518,15 @@ bool ADatabase::update(const AEntry &updated_entry)
 {
     auto data = updated_entry.getData();
     QString statement = QLatin1String("UPDATE ") + updated_entry.getPosition().tableName + QLatin1String(" SET ");
-    for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
+    RowData_T::const_iterator i;
+    for (i = data.constBegin(); i != data.constEnd(); ++i) {
         statement.append(i.key() + "=?,");
     }
     statement.chop(1);
     statement.append(QLatin1String(" WHERE ROWID=?"));
     QSqlQuery query;
     query.prepare(statement);
-    for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
+    for (i = data.constBegin(); i != data.constEnd(); ++i) {
         if (i.value() == QVariant(QString()) || i.value() == 0) {
             query.addBindValue(QVariant(QVariant::String));
         } else {
@@ -479,18 +549,50 @@ bool ADatabase::update(const AEntry &updated_entry)
     }
 }
 
+bool ADatabase::update(const OPL::Row &updated_row)
+{
+    QString statement = QLatin1String("UPDATE ") + OPL::GLOBALS->getDbTableName(updated_row.getTable()) + QLatin1String(" SET ");
+    for (auto i = updated_row.getRowData().constBegin(); i != updated_row.getRowData().constEnd(); ++i) {
+        statement.append(i.key() + "=?,");
+    }
+    statement.chop(1);
+    statement.append(QLatin1String(" WHERE ROWID=?"));
+    QSqlQuery query;
+    query.prepare(statement);
+    for (auto i = updated_row.getRowData().constBegin(); i != updated_row.getRowData().constEnd(); ++i) {
+        if (i.value() == QVariant(QString()) || i.value() == 0) {
+            query.addBindValue(QVariant(QVariant::String));
+        } else {
+            query.addBindValue(i.value());
+        }
+    }
+    query.addBindValue(updated_row.getRowId());
+
+    if (query.exec())
+    {
+        DEB << "Entry successfully committed.";
+        emit dataBaseUpdated();
+        return true;
+    } else {
+        DEB << "Unable to commit.";
+        DEB << "Query: " << statement;
+        DEB << "Query Error: " << query.lastError().text();
+        lastError = query.lastError();
+        return false;
+    }
+}
+
 bool ADatabase::insert(const AEntry &new_entry)
 {
-    auto data = new_entry.getData();
-    QString statement = QLatin1String("INSERT INTO ") + new_entry.getPosition().tableName + QLatin1String(" (");
-    QHash<QString, QVariant>::iterator i;
-    for (i = data.begin(); i != data.end(); ++i) {
+    QString statement = QLatin1String("INSERT INTO ") + new_entry.getTableName() + QLatin1String(" (");
+    RowData_T::const_iterator i;
+    for (auto i = new_entry.getData().constBegin(); i != new_entry.getData().constEnd(); ++i) {
         statement.append(i.key() + QLatin1Char(','));
     }
     statement.chop(1);
     statement += QLatin1String(") VALUES (");
 
-    for (int i=0; i < data.size(); ++i) {
+    for (int i=0; i < new_entry.getData().size(); ++i) {
         statement += QLatin1String("?,");
     }
     statement.chop(1);
@@ -499,7 +601,7 @@ bool ADatabase::insert(const AEntry &new_entry)
     QSqlQuery query;
     query.prepare(statement);
 
-    for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
+    for (i = new_entry.getData().constBegin(); i != new_entry.getData().constEnd(); ++i) {
         if (i.value() == QVariant(QString()) || i.value() == 0) {
             query.addBindValue(QVariant(QVariant::String));
         } else {
@@ -520,7 +622,48 @@ bool ADatabase::insert(const AEntry &new_entry)
         lastError = query.lastError();
         return false;
     }
+}
 
+bool ADatabase::insert(const OPL::Row &new_row)
+{
+    QString statement = QLatin1String("INSERT INTO ") + OPL::GLOBALS->getDbTableName(new_row.getTable()) + QLatin1String(" (");
+    QHash<QString, QVariant>::const_iterator i;
+    for (i = new_row.getRowData().constBegin(); i != new_row.getRowData().constEnd(); ++i) {
+        statement.append(i.key() + QLatin1Char(','));
+    }
+    statement.chop(1);
+    statement += QLatin1String(") VALUES (");
+
+    for (int i=0; i < new_row.getRowData().size(); ++i) {
+        statement += QLatin1String("?,");
+    }
+    statement.chop(1);
+    statement += QLatin1Char(')');
+
+    QSqlQuery query;
+    query.prepare(statement);
+
+    for (i = new_row.getRowData().constBegin(); i != new_row.getRowData().constEnd(); ++i) {
+        if (i.value() == QVariant(QString()) || i.value() == 0) {
+            query.addBindValue(QVariant(QVariant::String));
+        } else {
+            query.addBindValue(i.value());
+        }
+    }
+
+    //check result.
+    if (query.exec())
+    {
+        DEB << "Entry successfully committed.";
+        emit dataBaseUpdated();
+        return true;
+    } else {
+        DEB << "Unable to commit.";
+        DEB << "Query: " << statement;
+        DEB << "Query Error: " << query.lastError().text();
+        lastError = query.lastError();
+        return false;
+    }
 }
 
 RowData_T ADatabase::getEntryData(const DataPosition &data_position)
@@ -587,27 +730,91 @@ AEntry ADatabase::getEntry(const DataPosition &data_position)
     return entry;
 }
 
-APilotEntry ADatabase::getPilotEntry(RowId_T row_id)
+OPL::Row ADatabase::getRow(const OPL::DbTable table, const int row_id)
 {
-    APilotEntry pilot_entry(row_id);
-    pilot_entry.setData(getEntryData(pilot_entry.getPosition()));
-    return pilot_entry;
+    QString statement = QLatin1String("SELECT * FROM ") + OPL::GLOBALS->getDbTableName(table)
+            + QLatin1String(" WHERE ROWID=?");
+    QSqlQuery q;
+    q.prepare(statement);
+    q.addBindValue(row_id);
+    q.setForwardOnly(true);
+
+    if (!q.exec()) {
+        DEB << "SQL error: " << q.lastError().text();
+        DEB << "Statement: " << q.lastQuery();
+        lastError = q.lastError();
+        return {}; // return invalid Row
+    }
+
+    RowData_T entry_data;
+    if(q.next()) {
+        auto r = q.record(); // retreive record
+        if (r.count() == 0)  // row is empty
+            return {};
+
+        for (int i = 0; i < r.count(); i++){ // iterate through fields to get key:value map
+            if(!r.value(i).isNull()) {
+                entry_data.insert(r.fieldName(i), r.value(i));
+            }
+        }
+    }
+
+    return OPL::Row(table, row_id, entry_data);
 }
 
-ATailEntry ADatabase::getTailEntry(RowId_T row_id)
+RowData_T ADatabase::getRowData(const OPL::DbTable table, const int row_id)
 {
-    ATailEntry tail_entry(row_id);
-    tail_entry.setData(getEntryData(tail_entry.getPosition()));
-    return tail_entry;
+    QString statement = QLatin1String("SELECT * FROM ") + OPL::GLOBALS->getDbTableName(table)
+            + QLatin1String(" WHERE ROWID=?");
+    QSqlQuery q;
+    q.prepare(statement);
+    q.addBindValue(row_id);
+    q.setForwardOnly(true);
+
+    if (!q.exec()) {
+        DEB << "SQL error: " << q.lastError().text();
+        DEB << "Statement: " << q.lastQuery();
+        lastError = q.lastError();
+        return {}; // return invalid Row
+    }
+
+    RowData_T entry_data;
+    if(q.next()) {
+        auto r = q.record(); // retreive record
+        if (r.count() == 0)  // row is empty
+            return {};
+
+        for (int i = 0; i < r.count(); i++){ // iterate through fields to get key:value map
+            if(!r.value(i).isNull()) {
+                entry_data.insert(r.fieldName(i), r.value(i));
+            }
+        }
+    }
+
+    return entry_data;
 }
 
-AAircraftEntry ADatabase::getAircraftEntry(RowId_T row_id)
+APilotEntry ADatabase::getPilotEntry(RowId_T row_id)
 {
-    AAircraftEntry aircraft_entry(row_id);
-    aircraft_entry.setData(getEntryData(aircraft_entry.getPosition()));
-    return aircraft_entry;
+    APilotEntry pilot_entry(row_id);
+    pilot_entry.setData(getEntryData(pilot_entry.getPosition()));
+    return pilot_entry;
 }
 
+//OPL::TailEntry ADatabase::getTailEntry(RowId_T row_id)
+//{
+//    ATailEntry tail_entry(row_id);
+//    tail_entry.setData(getEntryData(tail_entry.getPosition()));
+//    return tail_entry;
+//}
+
+//OPL::AircraftEntry ADatabase::getAircraftEntry(RowId_T row_id)
+//{
+//    AAircraftEntry aircraft_entry(row_id);
+//    aircraft_entry.setData(getEntryData(aircraft_entry.getPosition()));
+//    return aircraft_entry;
+//}
+
 AFlightEntry ADatabase::getFlightEntry(RowId_T row_id)
 {
     AFlightEntry flight_entry(row_id);
@@ -795,10 +1002,10 @@ APilotEntry ADatabase::resolveForeignPilot(RowId_T foreign_key)
     return aDB->getPilotEntry(foreign_key);
 }
 
-ATailEntry ADatabase::resolveForeignTail(RowId_T foreign_key)
-{
-    return aDB->getTailEntry(foreign_key);
-}
+//ATailEntry ADatabase::resolveForeignTail(RowId_T foreign_key)
+//{
+//    return aDB->getTailEntry(foreign_key);
+//}
 
 QVector<QVariant> ADatabase::customQuery(QString statement, int return_values)
 {

+ 34 - 3
src/database/adatabase.h

@@ -35,11 +35,17 @@
 #include "src/classes/aentry.h"
 #include "src/classes/apilotentry.h"
 #include "src/classes/atailentry.h"
+
+#include "src/classes/row.h"
+#include "src/classes/tailentry.h"
+#include "src/classes/aircraftentry.h"
+
 #include "src/classes/aaircraftentry.h"
 #include "src/classes/aflightentry.h"
 #include "src/classes/astandardpaths.h"
 #include "src/classes/acurrencyentry.h"
 #include "src/classes/asimulatorentry.h"
+#include "src/classes/row.h"
 
 #define SQLITE_DRIVER QStringLiteral("QSQLITE")
 
@@ -252,6 +258,8 @@ public:
     bool exists(const AEntry &entry);
     bool exists(DataPosition data_position);
 
+    bool exists(const OPL::Row &row);
+
     /*!
      * \brief clear resets the database, i.e. deletes all content in the tables containing
      * userdata (pilots, flights, tails)
@@ -264,6 +272,8 @@ public:
      */
     bool commit(const AEntry &entry);
 
+    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.
@@ -276,16 +286,22 @@ public:
      */
     bool insert(const AEntry &new_entry);
 
+    bool insert(const OPL::Row &new_row);
+
     /*!
      * \brief Updates entry in database from existing entry tweaked by the user.
      */
     bool update(const AEntry &updated_entry);
 
+    bool update(const OPL::Row &updated_row);
+
     /*!
      * \brief deletes an entry from the database.
      */
     bool remove(const AEntry &entry);
 
+    bool remove(const OPL::Row &row);
+
     /*!
      * \brief deletes a list of entries from the database. Optimised for speed when
      * deleting many entries.
@@ -302,6 +318,13 @@ public:
      */
     AEntry getEntry(const DataPosition &data_position);
 
+    /*!
+     * \brief retreive a Row from the database
+     */
+    OPL::Row getRow(const OPL::DbTable table, const int row_id);
+
+    RowData_T getRowData(const OPL::DbTable table, const int row_id);
+
     /*!
      * \brief retreives a PilotEntry from the database.
      *
@@ -320,7 +343,11 @@ public:
      * instead of an Entry. It allows for easy access to a tail entry
      * with only the RowId required as input.
      */
-    ATailEntry getTailEntry(RowId_T row_id);
+    inline OPL::TailEntry getTailEntry(RowId_T row_id)
+    {
+        const auto data = getRowData(OPL::DbTable::Tails, row_id);
+        return OPL::TailEntry(row_id, data);
+    }
 
     /*!
      * \brief retreives a TailEntry from the database.
@@ -330,7 +357,11 @@ public:
      * instead of an AEntry. It allows for easy access to an aircraft entry
      * with only the RowId required as input.
      */
-    AAircraftEntry getAircraftEntry(RowId_T row_id);
+    inline OPL::AircraftEntry getAircraftEntry(RowId_T 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.
@@ -390,7 +421,7 @@ public:
      * \brief Resolves the foreign key in a flight entry
      * \return The Tail Entry referencted by the foreign key.
      */
-    ATailEntry resolveForeignTail(RowId_T foreign_key);
+    //ATailEntry resolveForeignTail(RowId_T foreign_key);
 
     /*!
      * \brief Return a summary of a database

+ 2 - 2
src/functions/acalc.cpp

@@ -314,7 +314,7 @@ void ACalc::updateAutoTimes(int acft_id)
     DEB << "Updating " << flight_list.length() << " flights with this aircraft.";
 
     auto acft = aDB->getTailEntry(acft_id);
-    auto acft_data = acft.getData();
+    auto acft_data = acft.getRowData();
     for (const auto& item : flight_list) {
         auto flight = aDB->getFlightEntry(item.toInt());
         auto flight_data = flight.getData();
@@ -326,7 +326,7 @@ void ACalc::updateAutoTimes(int acft_id)
             flight_data.insert(OPL::Db::FLIGHTS_TSPME, QString());
             flight_data.insert(OPL::Db::FLIGHTS_TMP, QString());
         } else if ((acft_data.value(OPL::Db::TAILS_MULTIPILOT) == 0
-                    && acft.getData().value(OPL::Db::TAILS_MULTIENGINE) == 1)) {
+                    && acft.getRowData().value(OPL::Db::TAILS_MULTIENGINE) == 1)) {
             DEB << "SPME";
             flight_data.insert(OPL::Db::FLIGHTS_TSPME, flight_data.value(OPL::Db::FLIGHTS_TBLK));
             flight_data.insert(OPL::Db::FLIGHTS_TSPSE, QString());

+ 3 - 3
src/gui/dialogues/newflightdialog.cpp

@@ -385,9 +385,9 @@ RowData_T NewFlightDialog::prepareFlightEntryData()
     // Aircraft
     int acft_id = completionData.tailsIdMap.key(ui->acftLineEdit->text());
     new_data.insert(OPL::Db::FLIGHTS_ACFT, acft_id);
-    const ATailEntry acft_data = aDB->getTailEntry(acft_id);
-    bool multi_pilot = acft_data.getData().value(OPL::Db::TAILS_MULTIPILOT).toBool();
-    bool multi_engine = acft_data.getData().value(OPL::Db::TAILS_MULTIENGINE).toBool();
+    const OPL::TailEntry acft_data = aDB->getTailEntry(acft_id);
+    bool multi_pilot = acft_data.getRowData().value(OPL::Db::TAILS_MULTIPILOT).toBool();
+    bool multi_engine = acft_data.getRowData().value(OPL::Db::TAILS_MULTIENGINE).toBool();
 
     if (multi_pilot) {
         new_data.insert(OPL::Db::FLIGHTS_TMP, block_minutes);

+ 6 - 9
src/gui/dialogues/newtaildialog.cpp

@@ -33,7 +33,7 @@ NewTailDialog::NewTailDialog(const QString &new_registration, QWidget *parent) :
     ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid blue"));
     ui->searchLineEdit->setFocus();
 
-    entry = ATailEntry();
+    //entry = OPL::TailEntry();
 }
 
 NewTailDialog::NewTailDialog(int row_id, QWidget *parent) :
@@ -102,12 +102,12 @@ void NewTailDialog::setupValidators()
 /*!
  * \brief NewTailDialog::fillForm populates the Dialog with the
  * information contained in an entry object. This can be either
- * a template (AAircraft, used when creating a new entry) or
- * a tail (ATail, used when editing an existing entry)
+ * a template (AircraftEntry, used when creating a new entry) or
+ * a tail (TailEntry, used when editing an existing entry)
  * \param is_template - determines whether we are adding a new entry
  * or editing an existing one.
  */
-void NewTailDialog::fillForm(AEntry entry, bool is_template)
+void NewTailDialog::fillForm(OPL::Row entry, bool is_template)
 {
     DEB << "Filling Form for a/c" << entry;
     //fill Line Edits
@@ -116,7 +116,7 @@ void NewTailDialog::fillForm(AEntry entry, bool is_template)
     if (is_template)
         line_edits.removeOne(ui->registrationLineEdit);
 
-    auto data = entry.getData();
+    auto data = entry.getRowData();
 
     for (const auto &le : qAsConst(line_edits)) {
         auto key = le->objectName().remove(QStringLiteral("LineEdit"));
@@ -202,7 +202,7 @@ void NewTailDialog::submitForm()
 
     //create db object
 
-    entry.setData(new_data);
+    entry.setRowData(new_data);
     LOG << "Commiting: " << entry;
     if (!aDB->commit(entry)) {
         QMessageBox message_box(this);
@@ -213,9 +213,6 @@ void NewTailDialog::submitForm()
         message_box.exec();
         return;
     } else {
-        if (entry.getPosition().rowId != 0)
-            ACalc::updateAutoTimes(entry.getPosition().rowId);
-
         QDialog::accept();
     }
 }

+ 7 - 2
src/gui/dialogues/newtaildialog.h

@@ -27,7 +27,10 @@
 #include "src/classes/asettings.h"
 #include "src/functions/acalc.h"
 #include "src/database/adatabase.h"
+
 #include "src/classes/atailentry.h"
+#include "src/classes/tailentry.h"
+
 #include "src/classes/aaircraftentry.h"
 
 namespace Ui {
@@ -79,7 +82,9 @@ private:
 
     Ui::NewTail *ui;
 
-    ATailEntry entry;
+    //ATailEntry entry;
+    OPL::TailEntry entry;
+
 
     QStringList aircraftList;
 
@@ -87,7 +92,7 @@ private:
 
     void setupCompleter();
     void setupValidators();
-    void fillForm(AEntry entry, bool is_template);
+    void fillForm(OPL::Row entry, bool is_template);
     bool verify();
     void submitForm();
 

+ 13 - 0
src/opl.h

@@ -115,6 +115,8 @@ enum class DbViewName {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly
  */
 enum class SimulatorType {FNPTI = 0, FNPTII = 1, FSTD = 2};
 
+enum class DbTable {Flights, Simulators, Pilots, Tails, Aircraft, Airports, Currencies};
+
 /*!
  * \brief The OplGlobals class encapsulates non-POD globals to avoid making them static. It is available
  * as a global static object via the OPL::GLOBAL makro and may be used as if it were a pointer, guaranteed to be initialized exactly once.
@@ -133,6 +135,7 @@ public:
     inline const QStringList &getApproachTypes() const {return APPROACH_TYPES;}
     inline const QString getLanguageFilePath(Translation language) const {return L10N_FilePaths.value(language);}
     inline const QString getViewIdentifier(DbViewName view_name) const {return DATABASE_VIEWS.value(view_name);}
+    inline const QString getDbTableName(DbTable table_name) const {return DB_TABLES.value(table_name);}
 
 private:
     Q_OBJECT
@@ -173,6 +176,16 @@ private:
         {SimulatorType::FNPTII, QStringLiteral("FNPT II")},
         {SimulatorType::FSTD,   QStringLiteral("FSTD")},
     };
+    const static inline QMap<DbTable, QString> DB_TABLES = {
+        //Flights, Simulators, Pilots, Tails, Aircraft, Airports
+        {DbTable::Flights,      QStringLiteral("flights")},
+        {DbTable::Simulators,   QStringLiteral("simulators")},
+        {DbTable::Pilots,       QStringLiteral("pilots")},
+        {DbTable::Tails,        QStringLiteral("tails")},
+        {DbTable::Aircraft,     QStringLiteral("aircraft")},
+        {DbTable::Airports,     QStringLiteral("airports")},
+        {DbTable::Currencies,   QStringLiteral("currencies")},
+    };
     const static inline QStringList APPROACH_TYPES = {
             QStringLiteral("VISUAL"),
             QStringLiteral("ILS CAT I"),