1 #pragma once
2 
3 #include <QtDebug>
4 #include <QtSql>
5 
6 
7 #define LOG_FAILED_QUERY(query) qWarning() << __FILE__ << __LINE__ << "FAILED QUERY [" \
8     << (query).executedQuery() << "]" << (query).lastError()
9 
10 class ScopedTransaction {
11   public:
ScopedTransaction(const QSqlDatabase & database)12     explicit ScopedTransaction(const QSqlDatabase& database) :
13             m_database(database),
14             m_active(false) {
15         if (!transaction()) {
16             qDebug() << "ERROR: Could not start transaction on"
17                      << m_database.connectionName();
18         }
19     }
~ScopedTransaction()20     virtual ~ScopedTransaction() {
21         if (m_active) {
22             rollback();
23         }
24     }
active()25     bool active() const {
26         return m_active;
27     }
transaction()28     bool transaction() {
29         if (m_active) {
30             qDebug() << "WARNING: Transaction already active and received transaction() request on"
31                      << m_database.connectionName();
32             return false;
33         }
34         m_active = m_database.transaction();
35         return m_active;
36     }
commit()37     bool commit() {
38         if (!m_active) {
39             qDebug() << "WARNING: commit() called on inactive transaction for"
40                      << m_database.connectionName();
41             return false;
42         }
43         bool result = m_database.commit();
44         qDebug() << "Committing transaction on"
45                  << m_database.connectionName()
46                  << "result:" << result;
47         m_active = false;
48         return result;
49     }
rollback()50     bool rollback() {
51         if (!m_active) {
52             qDebug() << "WARNING: rollback() called on inactive transaction for"
53                      << m_database.connectionName();
54             return false;
55         }
56         bool result = m_database.rollback();
57         qDebug() << "Rolling back transaction on"
58                  << m_database.connectionName()
59                  << "result:" << result;
60         m_active = false;
61         return result;
62     }
63   private:
64     QSqlDatabase m_database;
65     bool m_active;
66 };
67 
68 class FieldEscaper final {
69   public:
FieldEscaper(const QSqlDatabase & database)70     FieldEscaper(const QSqlDatabase& database)
71             : m_database(database),
72               m_stringField("string", QVariant::String) {
73     }
74 
75     // Escapes a string for use in a SQL query by wrapping with quotes and
76     // escaping embedded quote characters.
escapeString(const QString & escapeString)77     QString escapeString(const QString& escapeString) const {
78         m_stringField.setValue(escapeString);
79         return m_database.driver()->formatValue(m_stringField);
80     }
81 
escapeStrings(const QStringList & escapeStrings)82     QStringList escapeStrings(const QStringList& escapeStrings) const {
83         QStringList result = escapeStrings;
84         escapeStringsInPlace(&result);
85         return result;
86     }
87 
88   private:
escapeStringsInPlace(QStringList * pEscapeStrings)89     void escapeStringsInPlace(QStringList* pEscapeStrings) const {
90         QMutableStringListIterator it(*pEscapeStrings);
91         while (it.hasNext()) {
92             it.setValue(escapeString(it.next()));
93         }
94     }
95 
96     QSqlDatabase m_database;
97     mutable QSqlField m_stringField;
98 };
99