1 /*
2  * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #ifndef OWNSQL_H
20 #define OWNSQL_H
21 
22 #include <QLoggingCategory>
23 #include <QObject>
24 #include <QVariant>
25 
26 #include "ocsynclib.h"
27 
28 struct sqlite3;
29 struct sqlite3_stmt;
30 
31 namespace OCC {
Q_DECLARE_LOGGING_CATEGORY(lcSql)32 OCSYNC_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcSql)
33 
34 class SqlQuery;
35 
36 /**
37  * @brief The SqlDatabase class
38  * @ingroup libsync
39  */
40 class OCSYNC_EXPORT SqlDatabase
41 {
42     Q_DISABLE_COPY(SqlDatabase)
43 public:
44     explicit SqlDatabase();
45     ~SqlDatabase();
46 
47     bool isOpen();
48     bool openOrCreateReadWrite(const QString &filename);
49     bool openReadOnly(const QString &filename);
50     bool transaction();
51     bool commit();
52     void close();
53     QString error() const;
54     sqlite3 *sqliteDb();
55 
56 private:
57     enum class CheckDbResult {
58         Ok,
59         CantPrepare,
60         CantExec,
61         NotOk,
62     };
63 
64     bool openHelper(const QString &filename, int sqliteFlags);
65     CheckDbResult checkDb();
66 
67     sqlite3 *_db = nullptr;
68     QString _error; // last error string
69     int _errId = 0;
70 
71     friend class SqlQuery;
72     QSet<SqlQuery *> _queries;
73 };
74 
75 /**
76  * @brief The SqlQuery class
77  * @ingroup libsync
78  *
79  * There is basically 3 ways to initialize and use a query:
80  *
81     SqlQuery q1;
82     [...]
83     q1.initOrReset(...);
84     q1.bindValue(...);
85     q1.exec(...)
86  *
87     SqlQuery q2(db);
88     q2.prepare(...);
89     [...]
90     q2.reset_and_clear_bindings();
91     q2.bindValue(...);
92     q2.exec(...)
93  *
94     SqlQuery q3("...", db);
95     q3.bindValue(...);
96     q3.exec(...)
97  *
98  */
99 class OCSYNC_EXPORT SqlQuery
100 {
101     Q_DISABLE_COPY(SqlQuery)
102 public:
103     explicit SqlQuery() = default;
104     explicit SqlQuery(SqlDatabase &db);
105     explicit SqlQuery(const QByteArray &sql, SqlDatabase &db);
106     /**
107      * Prepare the SqlQuery.
108      * If the query was already prepared, this will first call finish(), and re-prepare it.
109      * This function must only be used if the constructor was setting a SqlDatabase
110      */
111     int prepare(const QByteArray &sql, bool allow_failure = false);
112 
113     ~SqlQuery();
114     QString error() const;
115     int errorId() const;
116 
117     /// Checks whether the value at the given column index is NULL
118     bool nullValue(int index);
119 
120     QString stringValue(int index);
121     int intValue(int index);
122     quint64 int64Value(int index);
123     QByteArray baValue(int index);
124     bool isSelect();
125     bool isPragma();
126     bool exec();
127 
128     struct NextResult
129     {
130         bool ok = false;
131         bool hasData = false;
132     };
133     NextResult next();
134 
135     template<class T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
bindValue(int pos,const T & value)136     void bindValue(int pos, const T &value)
137     {
138         qCDebug(lcSql) << "SQL bind" << pos << value;
139         bindValueInternal(pos, static_cast<int>(value));
140     }
141 
142     template<class T, typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
bindValue(int pos,const T & value)143     void bindValue(int pos, const T &value)
144     {
145         qCDebug(lcSql) << "SQL bind" << pos << value;
146         bindValueInternal(pos, value);
147     }
148 
bindValue(int pos,const QByteArray & value)149     void bindValue(int pos, const QByteArray &value)
150     {
151         qCDebug(lcSql) << "SQL bind" << pos << QString::fromUtf8(value);
152         bindValueInternal(pos, value);
153     }
154 
155     const QByteArray &lastQuery() const;
156     int numRowsAffected();
157     void reset_and_clear_bindings();
158 
159 private:
160     void bindValueInternal(int pos, const QVariant &value);
161     void finish();
162 
163     SqlDatabase *_sqldb = nullptr;
164     sqlite3 *_db = nullptr;
165     sqlite3_stmt *_stmt = nullptr;
166     QString _error;
167     int _errId;
168     QByteArray _sql;
169 
170     friend class SqlDatabase;
171     friend class PreparedSqlQueryManager;
172 };
173 
174 } // namespace OCC
175 
176 #endif // OWNSQL_H
177