Adding notification feature to SQLite driver
This modification enables to use notification feature of SQLite with Qt SQL driver, enables to subscribe for notifications and also to remove notifications. close() is added to destructor to unregister notifications in case it is used in multiple threads. [ChangeLog][QtSql][SQLite] Adding notification feature to SQLite driver Change-Id: I8b98787f5214a406357646a98711a8ff6045a0dd Reviewed-by: Andy Shaw <andy.shaw@theqtcompany.com>
This commit is contained in:
parent
2be25273e1
commit
b7ef2510d1
@ -59,6 +59,7 @@
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <functional>
|
||||
|
||||
Q_DECLARE_OPAQUE_POINTER(sqlite3*)
|
||||
Q_DECLARE_METATYPE(sqlite3*)
|
||||
@ -140,6 +141,7 @@ public:
|
||||
inline QSQLiteDriverPrivate() : QSqlDriverPrivate(), access(0) { dbmsType = QSqlDriver::SQLite; }
|
||||
sqlite3 *access;
|
||||
QList <QSQLiteResult *> results;
|
||||
QStringList notificationid;
|
||||
};
|
||||
|
||||
|
||||
@ -571,6 +573,7 @@ QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent)
|
||||
|
||||
QSQLiteDriver::~QSQLiteDriver()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::hasFeature(DriverFeature f) const
|
||||
@ -585,11 +588,11 @@ bool QSQLiteDriver::hasFeature(DriverFeature f) const
|
||||
case SimpleLocking:
|
||||
case FinishQuery:
|
||||
case LowPrecisionNumbers:
|
||||
case EventNotifications:
|
||||
return true;
|
||||
case QuerySize:
|
||||
case NamedPlaceholders:
|
||||
case BatchOperations:
|
||||
case EventNotifications:
|
||||
case MultipleResultSets:
|
||||
case CancelQuery:
|
||||
return false;
|
||||
@ -664,9 +667,13 @@ void QSQLiteDriver::close()
|
||||
for (QSQLiteResult *result : qAsConst(d->results))
|
||||
result->d_func()->finalize();
|
||||
|
||||
if (d->access && (d->notificationid.count() > 0)) {
|
||||
d->notificationid.clear();
|
||||
sqlite3_update_hook(d->access, NULL, NULL);
|
||||
}
|
||||
|
||||
if (sqlite3_close(d->access) != SQLITE_OK)
|
||||
setLastError(qMakeError(d->access, tr("Error closing database"),
|
||||
QSqlError::ConnectionError));
|
||||
setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError));
|
||||
d->access = 0;
|
||||
setOpen(false);
|
||||
setOpenError(false);
|
||||
@ -825,4 +832,72 @@ QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierTyp
|
||||
return _q_escapeIdentifier(identifier);
|
||||
}
|
||||
|
||||
static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,
|
||||
sqlite3_int64 arowid)
|
||||
{
|
||||
Q_UNUSED(aoperation);
|
||||
Q_UNUSED(adbname);
|
||||
QSQLiteDriver *driver = static_cast<QSQLiteDriver *>(qobj);
|
||||
if (driver) {
|
||||
QMetaObject::invokeMethod(driver, "handleNotification", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromUtf8(atablename)), Q_ARG(qint64, arowid));
|
||||
}
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::subscribeToNotification(const QString &name)
|
||||
{
|
||||
Q_D(QSQLiteDriver);
|
||||
if (!isOpen()) {
|
||||
qWarning("Database not open.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->notificationid.contains(name)) {
|
||||
qWarning("Already subscribing to '%s'.", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
//sqlite supports only one notification callback, so only the first is registered
|
||||
d->notificationid << name;
|
||||
if (d->notificationid.count() == 1)
|
||||
sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::unsubscribeFromNotification(const QString &name)
|
||||
{
|
||||
Q_D(QSQLiteDriver);
|
||||
if (!isOpen()) {
|
||||
qWarning("Database not open.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->notificationid.contains(name)) {
|
||||
qWarning("Not subscribed to '%s'.", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
d->notificationid.removeAll(name);
|
||||
if (d->notificationid.isEmpty())
|
||||
sqlite3_update_hook(d->access, NULL, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList QSQLiteDriver::subscribedToNotifications() const
|
||||
{
|
||||
Q_D(const QSQLiteDriver);
|
||||
return d->notificationid;
|
||||
}
|
||||
|
||||
void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid)
|
||||
{
|
||||
Q_D(const QSQLiteDriver);
|
||||
if (d->notificationid.contains(tableName)) {
|
||||
emit notification(tableName);
|
||||
emit notification(tableName, QSqlDriver::UnknownSource, QVariant(rowid));
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -93,6 +93,12 @@ public:
|
||||
QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE;
|
||||
QVariant handle() const Q_DECL_OVERRIDE;
|
||||
QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE;
|
||||
bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE;
|
||||
QStringList subscribedToNotifications() const Q_DECL_OVERRIDE;
|
||||
private Q_SLOTS:
|
||||
void handleNotification(const QString &tableName, qint64 rowid);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -100,6 +100,8 @@ private slots:
|
||||
void eventNotificationIBase();
|
||||
void eventNotificationPSQL_data() { generic_data("QPSQL"); }
|
||||
void eventNotificationPSQL();
|
||||
void eventNotificationSQLite_data() { generic_data("QSQLITE"); }
|
||||
void eventNotificationSQLite();
|
||||
|
||||
//database specific 64 bit integer test
|
||||
void bigIntField_data() { generic_data(); }
|
||||
@ -2109,6 +2111,35 @@ void tst_QSqlDatabase::eventNotificationPSQL()
|
||||
QVERIFY_SQL(driver, unsubscribeFromNotification(procedureName));
|
||||
}
|
||||
|
||||
void tst_QSqlDatabase::eventNotificationSQLite()
|
||||
{
|
||||
QFETCH(QString, dbName);
|
||||
QSqlDatabase db = QSqlDatabase::database(dbName);
|
||||
CHECK_DATABASE(db);
|
||||
if (db.driverName().compare(QLatin1String("QSQLITE"), Qt::CaseInsensitive)) {
|
||||
QSKIP("QSQLITE specific test");
|
||||
}
|
||||
const QString tableName(qTableName("sqlitnotifytest", __FILE__, db));
|
||||
tst_Databases::safeDropTable(db, tableName);
|
||||
|
||||
QSignalSpy notificationSpy(db.driver(), SIGNAL(notification(QString)));
|
||||
QSignalSpy notificationSpyExt(db.driver(), SIGNAL(notification(QString,QSqlDriver::NotificationSource,QVariant)));
|
||||
QSqlQuery q(db);
|
||||
QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER, realVal REAL)"));
|
||||
db.driver()->subscribeToNotification(tableName);
|
||||
QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, realVal) VALUES (1, 2.3)"));
|
||||
QTRY_COMPARE(notificationSpy.count(), 1);
|
||||
QTRY_COMPARE(notificationSpyExt.count(), 1);
|
||||
QList<QVariant> arguments = notificationSpy.takeFirst();
|
||||
QCOMPARE(arguments.at(0).toString(), tableName);
|
||||
arguments = notificationSpyExt.takeFirst();
|
||||
QCOMPARE(arguments.at(0).toString(), tableName);
|
||||
db.driver()->unsubscribeFromNotification(tableName);
|
||||
QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, realVal) VALUES (1, 2.3)"));
|
||||
QTRY_COMPARE(notificationSpy.count(), 0);
|
||||
QTRY_COMPARE(notificationSpyExt.count(), 0);
|
||||
}
|
||||
|
||||
void tst_QSqlDatabase::sqlite_bindAndFetchUInt()
|
||||
{
|
||||
QFETCH(QString, dbName);
|
||||
|
Loading…
x
Reference in New Issue
Block a user