Public Slots | Signals | Public Member Functions | Static Public Member Functions | Protected Slots | Protected Member Functions

Database Class Reference

The main database object used throughout the program. More...

#include <Database.h>

Inheritance diagram for Database:
Inheritance graph
[legend]
Collaboration diagram for Database:
Collaboration graph
[legend]

List of all members.

Public Slots

void slot_queryThreadResult (QueryThreadCommand result)
void pipe_queryResult (QSqlQuery *)

Signals

void signal_queryQueue_changed (QueryQueue *queue)
void tableChanged (const QString &tableName)

Public Member Functions

void execThreaded (const QString &query, const QObject *sender)
void execThreaded (const QueryThreadCommand &cmd)

Static Public Member Functions

static Databaseinstance ()
static QSqlDatabase staticCon ()
static QSqlQuery execStatic (const QString &query=QString())
static QSqlRecord execStaticR (const QString &query)
static ProgressToolBarprogressToolBar ()
static bool execPending (const QObject *sender)
static QSqlIndex lastInserted (const QString &tableName)
static void listenForTableChange (const QString &tableName)
static void notifyTableChange (const QString &tableName)

Protected Slots

void onDriverNotification (const QString &name)

Protected Member Functions

 Database ()
 Database (const QString &driver, const QString &dbName, const QString &host, const QString &user, const QString &passwd, int port)
 ~Database ()

Detailed Description

The main database object used throughout the program.

A cross-application notification system is implemented with NOTIFY. It could possibly be extended with a more generic database-centralized system.


Constructor & Destructor Documentation

Database::Database (  )  [protected]

{
    ConnectionDialog d;
    if (d.exec() != QDialog::Accepted)
        // temporary
        threadSetup("QPSQL", "smartlet" , "pleco10.ugent.be", "smartlet", "hent", 5432);
    else
        threadSetup(d.driverName(), d.databaseName(), d.hostName(),
                    d.userName(), d.password(), d.port());

};

Database::Database ( const QString driver,
const QString dbName,
const QString host,
const QString user,
const QString passwd,
int  port 
) [protected]

{
    threadSetup(driver, dbName, host, user, passwd, port);
}

Database::~Database (  )  [protected]

                   {
    qDebug() << "Stopping Database thread...";
    thread->quit();
    if (thread->wait(10000))
        qDebug() << "Thread stopped successfully";
    else
        qDebug() << "Thread terminated by force";
};


Member Function Documentation

bool Database::execPending ( const QObject sender  )  [static]

This function should be (is?) treadsafe as it first checks the queue from which the threads take data, and then check the executing thread(s).

{
    return Database::instance()->queryQueue.contains(QueryThreadCommand(const_cast<QObject*>(sender))) ||
            Database::instance()->thread->currentCmd().sender() == sender;
}

QSqlQuery Database::execStatic ( const QString query = QString()  )  [static]

Executes the query in the same thread the application is running.

Todo:
Max execution time? And an isSuccessfull by reference bool, to check whether all went well.

Currently, there is no limit set on the duration of execution. This could be a problem in case of bad connection or connection drops. A solution to this problem could be a threaded execetution with a time limit. A possible implementation to start with is described here. Problem is that is such cases we would need to open a third connection because such connection could not be used in Database::staticCon() which is used in many models. A better approach would then probably be to use the existing QueryThread and implement some priority mechanism or similar? Check here for statement_timeout. This could be set dynamicaly each time on MainConnection, or it could be set once for max of 5 sec or similar. This should best be tested.

Referenced by BaseQueryModel::BaseQueryModel(), ProjectTreeModel::data(), execStaticR(), FilesUploadedModel::FilesUploadedModel(), notifyTableChange(), SensorUnitEditor::onAddButtonPress(), SqlDataStrategy::reconfigure(), ProjectTreeModel::removeRows(), CalibrationsModel::reQuery(), SensorUnitEditor::SensorUnitEditor(), ConvertableUnitModel::setConstraint(), SensorChannelModel::setConstraint(), SensorChannelCalibrationsView::setConstraint(), ProjectTreeModel::setData(), SqlUnitConvertor::SqlUnitConvertor(), TemplatesModel::TemplatesModel(), ConvertableUnitModel::unconstrain(), SensorChannelModel::unconstrain(), and UnitConversionEditor::UnitConversionEditor().

{
    if (query.isNull() || query.isEmpty()) return QSqlQuery();
    if (staticCon().isOpenError() || !staticCon().isOpen()){
        qDebug() << "Static connection to database not open: can not execute query.";
        return QSqlQuery();
    }
    //qDebug() << query;
    static QSqlQuery q(staticCon());
    if (q.exec(query)) return q;
    else {
        qWarning() << "Error from statically executed query: " << q.lastError().text()
                << "\nfrom query: " << query;
        return q;
    }
}

QSqlRecord Database::execStaticR ( const QString query  )  [static]

Exectues a SELECT query and expects zero or one record to be returned.

This is mainly a convenience function for queries which fetch certain data from the database.

Referenced by lastInserted(), ProjectCalibrationEditor::onNewButtonPress(), SensorChannelCalibrationEditor::SensorChannelCalibrationEditor(), and MappedBaseEditor::setTable().

{
    QSqlQuery q = execStatic(query);

    // sanity checks
    if (!q.isActive() || !q.isSelect()) {
        if (!staticCon().isOpenError() && staticCon().isOpen())
            qDebug() << "Query \"" << query << "\"  retuned activestate: " << q.isActive() << " and selectstate: " <<  q.isSelect();
        return QSqlRecord();
    }

    if (!(q.size() <= 1)){
        qDebug() << "Query " << query << " returned " << q.size() << " rows where it only must return 0 or 1.";
        Q_ASSERT(false);
    }

    if (q.size() == 0)
        return QSqlRecord();

    if (!q.next()) //position on valid record;
        return QSqlRecord();

    Q_ASSERT(q.isValid()); // true if the query is currently positioned on a valid record

    // record ready and valid for return
    return q.record();
}

void Database::execThreaded ( const QString query,
const QObject sender 
)

Referenced by DatabaseAuditLogView::updateModel().

{
    // setup command
    QueryThreadCommand cmd(sender, query);
    execThreaded(cmd);
}

void Database::execThreaded ( const QueryThreadCommand cmd  ) 

{
    Q_ASSERT(cmd.isValid());
    // Check whether sender can receive the result: it needs an implementation
    // of slot pipe_queryResult(QSqlQuery&)
    if (cmd.sender()->metaObject()->indexOfSlot("pipe_queryResult(QSqlQuery*)") == -1) {
        qWarning() << cmd.sender()->objectName() << " requested a threaded execution of query but has no pipe to fetch the result";
        return;
    }

    // put command in queue
    QueryQueue *queue = &queryQueue;
    queue->enqueue(cmd);

    // cancel any query currently in execution from m_sender
    cancelQuery(cmd.sender());

    // signal that queue has changed
    emit signal_queryQueue_changed(queue);
}

Database * Database::instance (  )  [static]
QSqlIndex Database::lastInserted ( const QString tableName  )  [static]

Referenced by MappedBaseEditor::onSubmitButtonPress().

{
    QSqlRecord result = Database::execStaticR("SELECT * FROM fnc_get_last_inserted_seq_id('" + tableName + "'::regclass)");

    if (result.isEmpty() || result.isNull("column_name") || result.isNull("seq_number")) return QSqlIndex();

    QSqlField field(result.value("column_name").toString(), QVariant::Int);

    QSqlIndex index;
    index.append(field);
    index.setValue(0, result.value("seq_number"));

    qDebug() << "returning last inserted id: " << index << " for table " << tableName;

    return index;
}

void Database::listenForTableChange ( const QString tableName  )  [static]

Sets the database driver to listen for table change notifications. If such change occurs, Database::tableChanged() is emitted.

See also:
Database::tableChanged()
GlobalConst::DB_TABLE_CHANGE_NOTIFY()

Referenced by SqlHelper::setDependency().

{
    if ((Database::staticCon().tables(QSql::AllTables).filter(QRegExp(".*" + tableName)).count() < 1) && (Database::staticCon().isOpen())){
        qWarning() << "Trying to listen for table changes on " << tableName << ", but the table doesn't exist.";
    }

    // Setup notification
    // Once made, this notification is never unsubscribed because it is
    // unknown how many SqlTableModels there are based on the same table.
    QString notificationName(GlobalConst::DB_TABLE_CHANGE_NOTIFY.arg(tableName));
    if (!Database::staticCon().driver()->subscribedToNotifications().contains(notificationName))
        Database::staticCon().driver()->subscribeToNotification(notificationName);
}

void Database::notifyTableChange ( const QString tableName  )  [static]

This function is called by application objects to inform the Database and other concurrent applications that the application is finished changing/adjusting the table.

Referenced by MappedBaseEditor::onDeleteButtonPress(), TableBaseEditor::onSubmitButtonPress(), MappedBaseEditor::onSubmitButtonPress(), and ProjectTreeModel::setData().

{
    QString notifyCmd("NOTIFY \"" + GlobalConst::DB_TABLE_CHANGE_NOTIFY.arg(tableName) + "\""); // " is mandatory because of Qt's specific LISTEN driver implementation
    qDebug() << notifyCmd << " executed.";
    execStatic(notifyCmd);
}

void Database::onDriverNotification ( const QString name  )  [protected, slot]

{
    qDebug() << name << QString(name).remove(GlobalConst::DB_TABLE_CHANGE_NOTIFY.arg(""));
    if (name.contains(GlobalConst::DB_TABLE_CHANGE_NOTIFY.arg(""))){
        QString tableName = QString(name).remove(GlobalConst::DB_TABLE_CHANGE_NOTIFY.arg(""));
        emit tableChanged(tableName);
    }
}

void Database::pipe_queryResult ( QSqlQuery  )  [slot]

{
    Q_ASSERT_X(false, "Database", "unimplemented function Database::pipe_queryResult()");
}

ProgressToolBar * Database::progressToolBar (  )  [static]

Referenced by main().

{
    return Database::instance()->m_progressToolBar;
}

void Database::signal_queryQueue_changed ( QueryQueue queue  )  [signal]

Referenced by execThreaded().

void Database::slot_queryThreadResult ( QueryThreadCommand  result  )  [slot]

{
    // Best would be to pass through the QSharedPointer<QSqlQuery> because the source might be deleted!
    QMetaObject::invokeMethod(const_cast<QObject*>(result.sender()), "pipe_queryResult", Qt::DirectConnection, Q_ARG(QSqlQuery*, result.result()));
}

QSqlDatabase Database::staticCon (  )  [static]
void Database::tableChanged ( const QString tableName  )  [signal]

Referenced by onDriverNotification().


The documentation for this class was generated from the following files: