From 2a3297c7269c9a3a9e6b4129c76366bac5c9d7cb Mon Sep 17 00:00:00 2001 From: Lorenz Haas Date: Sat, 19 Nov 2016 19:05:36 +0100 Subject: REGEXP for SQLite Since SQLite does not define a regexp function by default, provide a Qt based implementation which can be enabled using QSQLITE_ENABLE_REGEXP as an connect option. This way statements like SELECT * FROM table WHERE col REGEXP '^[a-d]'; work out of the box. [ChangeLog][QtSql] Add QSQLITE_ENABLE_REGEXP connect option for QSQLiteDriver. If set a Qt based regexp() implementation is provided allowing to use REGEXP in SQL statements. Task-number: QTBUG-18084 Change-Id: I7f0e926fe4c5d6baea509f75497f46a61ca86679 Reviewed-by: Milian Wolff Reviewed-by: Jesus Fernandez Reviewed-by: Sebastian Sauer Reviewed-by: Andy Shaw Reviewed-by: Frederik Gladhorn --- src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'src/plugins/sqldrivers/sqlite') diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp index 1cb0f10494..56eceeecbd 100644 --- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp @@ -51,6 +51,10 @@ #include #include #include +#ifndef QT_NO_REGULAREXPRESSION +#include +#include +#endif #if defined Q_OS_WIN # include @@ -556,6 +560,37 @@ QVariant QSQLiteResult::handle() const ///////////////////////////////////////////////////////// +#ifndef QT_NO_REGULAREXPRESSION +static void _q_regexp(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + if (Q_UNLIKELY(argc != 2)) { + sqlite3_result_int(context, 0); + return; + } + + const QString pattern = QString::fromUtf8( + reinterpret_cast(sqlite3_value_text(argv[0]))); + const QString subject = QString::fromUtf8( + reinterpret_cast(sqlite3_value_text(argv[1]))); + + auto cache = static_cast*>(sqlite3_user_data(context)); + QRegularExpression *regexp = cache->object(pattern); + if (!regexp) { + regexp = new QRegularExpression(pattern, QRegularExpression::DontCaptureOption + | QRegularExpression::OptimizeOnFirstUsageOption); + cache->insert(pattern, regexp); + } + const bool found = subject.contains(*regexp); + + sqlite3_result_int(context, int(found)); +} + +static void _q_regexp_cleanup(void *cache) +{ + delete static_cast*>(cache); +} +#endif + QSQLiteDriver::QSQLiteDriver(QObject * parent) : QSqlDriver(*new QSQLiteDriverPrivate, parent) { @@ -615,6 +650,11 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c bool sharedCache = false; bool openReadOnlyOption = false; bool openUriOption = false; +#ifndef QT_NO_REGULAREXPRESSION + static const QLatin1String regexpConnectOption = QLatin1String("QSQLITE_ENABLE_REGEXP"); + bool defineRegexp = false; + int regexpCacheSize = 25; +#endif const auto opts = conOpts.splitRef(QLatin1Char(';')); for (auto option : opts) { @@ -634,6 +674,22 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) { sharedCache = true; } +#ifndef QT_NO_REGULAREXPRESSION + else if (option.startsWith(regexpConnectOption)) { + option = option.mid(regexpConnectOption.size()).trimmed(); + if (option.isEmpty()) { + defineRegexp = true; + } else if (option.startsWith(QLatin1Char('='))) { + bool ok = false; + const int cacheSize = option.mid(1).trimmed().toInt(&ok); + if (ok) { + defineRegexp = true; + if (cacheSize > 0) + regexpCacheSize = cacheSize; + } + } + } +#endif } int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); @@ -646,6 +702,13 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c sqlite3_busy_timeout(d->access, timeOut); setOpen(true); setOpenError(false); +#ifndef QT_NO_REGULAREXPRESSION + if (defineRegexp) { + auto cache = new QCache(regexpCacheSize); + sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache, &_q_regexp, NULL, + NULL, &_q_regexp_cleanup); + } +#endif return true; } else { if (d->access) { -- cgit v1.2.3