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 &currentStart, const QString::const_iterator &currentPosition) {
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