1 /*
2 SPDX-FileCopyrightText: 2014 Ivan Cukic <ivan.cukic@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #ifndef COMMON_DATABASE_H
8 #define COMMON_DATABASE_H
9
10 #include <QRegularExpression>
11 #include <QSqlQuery>
12 #include <memory>
13 #include <utils/d_ptr.h>
14
15 namespace Common
16 {
17 class Database
18 {
19 public:
20 typedef std::shared_ptr<Database> Ptr;
21
22 enum Source {
23 ResourcesDatabase,
24 };
25
26 enum OpenMode {
27 ReadWrite,
28 ReadOnly,
29 };
30
31 static Ptr instance(Source source, OpenMode openMode);
32
33 QSqlQuery execQueries(const QStringList &queries) const;
34 QSqlQuery execQuery(const QString &query) const;
35 QSqlQuery createQuery() const;
36
37 void setPragma(const QString &pragma);
38 QVariant pragma(const QString &pragma) const;
39 QVariant value(const QString &query) const;
40
41 ~Database();
42 Database();
43
44 friend class Locker;
45 class Locker
46 {
47 public:
48 Locker(Database &database);
49 ~Locker();
50
51 private:
52 QSqlDatabase &m_database;
53 };
54
55 #define DATABASE_TRANSACTION(A) \
56 /* enable this for debugging only: qDebug() << "Location:" << __FILE__ << __LINE__; */ \
57 Common::Database::Locker lock(A)
58
59 private:
60 D_PTR;
61 };
62
63 template<typename EscapeFunction>
parseStarPattern(const QString & pattern,const QString & joker,EscapeFunction escape)64 QString parseStarPattern(const QString &pattern, const QString &joker, EscapeFunction escape)
65 {
66 const auto begin = pattern.constBegin();
67 const auto end = pattern.constEnd();
68
69 auto currentStart = pattern.constBegin();
70 auto currentPosition = pattern.constBegin();
71
72 bool isEscaped = false;
73
74 // This should be available in the QString class...
75 auto stringFromIterators = [&](const QString::const_iterator ¤tStart, const QString::const_iterator ¤tPosition) {
76 return pattern.mid(std::distance(begin, currentStart), std::distance(currentStart, currentPosition));
77 };
78
79 // Escaping % and _ for sql like
80 // auto escape = [] (QString str) {
81 // return str.replace("%", "\\%").replace("_", "\\_");
82 // };
83
84 QString resultPattern;
85 resultPattern.reserve(pattern.size() * 1.5);
86
87 for (; currentPosition != end; ++currentPosition) {
88 if (isEscaped) {
89 // Just skip the current character
90 isEscaped = false;
91
92 } else if (*currentPosition == QLatin1Char('\\')) {
93 // Skip two characters
94 isEscaped = true;
95
96 } else if (*currentPosition == QLatin1Char('*')) {
97 // Replacing the star with the sql like joker - %
98 resultPattern.append(escape(stringFromIterators(currentStart, currentPosition)) + joker);
99 currentStart = currentPosition + 1;
100
101 } else {
102 // This one is boring, nothing to do
103 }
104 }
105
106 if (currentStart != currentPosition) {
107 resultPattern.append(escape(stringFromIterators(currentStart, currentPosition)));
108 }
109
110 return resultPattern;
111 }
112
escapeSqliteLikePattern(QString pattern)113 inline QString escapeSqliteLikePattern(QString pattern)
114 {
115 return pattern.replace(QLatin1String("%"), QLatin1String("\\%")).replace(QLatin1String("_"), QLatin1String("\\_"));
116 }
117
starPatternToLike(const QString & pattern)118 inline QString starPatternToLike(const QString &pattern)
119 {
120 return parseStarPattern(pattern, QStringLiteral("%"), escapeSqliteLikePattern);
121 }
122
starPatternToRegex(const QString & pattern)123 inline QRegularExpression starPatternToRegex(const QString &pattern)
124 {
125 const QString parsed = parseStarPattern(pattern, QStringLiteral(".*"), QOverload<const QString &>::of(&QRegularExpression::escape));
126 return QRegularExpression(QRegularExpression::anchoredPattern(parsed));
127 }
128
129 } // namespace Common
130
131 #endif // COMMON_DATABASE_H
132