summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp81
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h6
-rw-r--r--tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp31
3 files changed, 115 insertions, 3 deletions
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
index 2a45b73d14..ef4ef2e93c 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
@@ -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
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
index c9b7708698..ca969a4b53 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
@@ -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
diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp
index bd8fbcca0e..8126e72ad2 100644
--- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp
+++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp
@@ -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);