src/zserio/SqliteConnection.h
Line | Count | Source |
1 | | #ifndef ZSERIO_SQL_CONNECTION_H_INC |
2 | | #define ZSERIO_SQL_CONNECTION_H_INC |
3 | | |
4 | | #include "sqlite3.h" |
5 | | |
6 | | #include <memory> |
7 | | |
8 | | #include "zserio/StringView.h" |
9 | | #include "zserio/SqliteException.h" |
10 | | #include "zserio/SqliteFinalizer.h" |
11 | | |
12 | | namespace zserio |
13 | | { |
14 | | |
15 | | /** |
16 | | * Helper class to keep sqlite3 connection and ensure its safe destruction. |
17 | | * |
18 | | * The class also provides simple interface to execute SQLite queries. |
19 | | */ |
20 | | class SqliteConnection |
21 | | { |
22 | | public: |
23 | | /** |
24 | | * Connection type. |
25 | | */ |
26 | | enum ConnectionType |
27 | | { |
28 | | INTERNAL_CONNECTION, /**< Internal connection which must be released in destructor. */ |
29 | | EXTERNAL_CONNECTION /**< External connection managed from outside. */ |
30 | | }; |
31 | | |
32 | | /** |
33 | | * Constructor. |
34 | | * |
35 | | * \param connection Pointer to the SQLite connection. |
36 | | * \param connectionType Type of the connection. Default is INTERNAL_CONNECTION. |
37 | | */ |
38 | | explicit SqliteConnection(sqlite3* connection = nullptr, |
39 | | ConnectionType connectionType = INTERNAL_CONNECTION); |
40 | | |
41 | | /** |
42 | | * Destructor. |
43 | | */ |
44 | | ~SqliteConnection(); |
45 | | |
46 | | /** |
47 | | * Copying and moving is disallowed! |
48 | | * \{ |
49 | | */ |
50 | | SqliteConnection(const SqliteConnection&) = delete; |
51 | | SqliteConnection& operator=(const SqliteConnection&) = delete; |
52 | | |
53 | | SqliteConnection(SqliteConnection&&) = delete; |
54 | | SqliteConnection& operator=(SqliteConnection&&) = delete; |
55 | | /** \} */ |
56 | | |
57 | | /** |
58 | | * Resets the connection. |
59 | | * |
60 | | * \param connection New connection to set. Default is NULL - i.e. unset. |
61 | | * \param connectionType Type of the new connection. |
62 | | */ |
63 | | void reset(sqlite3* connection = nullptr, ConnectionType connectionType = INTERNAL_CONNECTION); |
64 | | |
65 | | /** |
66 | | * Gets the current connection type. |
67 | | * |
68 | | * When connection is NULL, the connection type is insignificant. |
69 | | * |
70 | | * \return Connection type. |
71 | | */ |
72 | | ConnectionType getConnectionType() const; |
73 | | |
74 | | /** |
75 | | * Gets the current connection. |
76 | | * |
77 | | * \return SQLite connection. |
78 | | */ |
79 | | sqlite3* getConnection(); |
80 | | |
81 | | /** |
82 | | * Executes a single query which doesn't need to return anything - e.g. DML. |
83 | | * |
84 | | * \param sqlQuery The query string. |
85 | | */ |
86 | | void executeUpdate(StringView sqlQuery); |
87 | | |
88 | | /** |
89 | | * Prepares the SQLite statement for the given query. |
90 | | * |
91 | | * Note that the user is responsible to proper statement finalization using sqlite3_finalize! |
92 | | * |
93 | | * \param sqlQuery The query string. |
94 | | * |
95 | | * \return Prepared SQLite statement. |
96 | | */ |
97 | | sqlite3_stmt* prepareStatement(StringView sqlQuery); |
98 | | |
99 | | /** |
100 | | * Starts a new transaction if a transaction is not already started. |
101 | | * |
102 | | * \return True when the new transaction was started. False when a transaction is already started. |
103 | | */ |
104 | | bool startTransaction(); |
105 | | |
106 | | /** |
107 | | * Terminates the current transaction. |
108 | | * |
109 | | * The parameter wasTransactionStarted is used for convenience since it's then easier to write code |
110 | | * which uses transactions. |
111 | | * |
112 | | * \code{.cpp} |
113 | | * bool wasTransactionStarted = connection.startTransaction(); // transaction may be already started |
114 | | * // execute queries |
115 | | * // ... |
116 | | * // terminates the transaction only if it was started by the corresponding startTransaction call. |
117 | | * connection.endTransaction(wasTransactionStarted); |
118 | | * \endcode |
119 | | * |
120 | | * \param wasTransactionStarted When false, the call does actually nothing. |
121 | | */ |
122 | | void endTransaction(bool wasTransactionStarted); |
123 | | |
124 | | private: |
125 | | sqlite3* m_connection; |
126 | | ConnectionType m_connectionType; |
127 | | }; |
128 | | |
129 | | inline SqliteConnection::SqliteConnection(sqlite3* connection, ConnectionType connectionType) |
130 | | : m_connection(connection), m_connectionType(connectionType) |
131 | 20 | {} |
132 | | |
133 | | inline SqliteConnection::~SqliteConnection() |
134 | 20 | { |
135 | 20 | reset(); |
136 | 20 | } |
137 | | |
138 | | inline void SqliteConnection::reset(sqlite3* connection, ConnectionType connectionType) |
139 | 40 | { |
140 | | // close connection only if it is internal |
141 | 40 | if (m_connectionType == INTERNAL_CONNECTION) |
142 | 35 | sqlite3_close_v2(m_connection); // sqlite3_close_v2(NULL) is a harmless no-op |
143 | | |
144 | 40 | m_connection = connection; |
145 | 40 | m_connectionType = connectionType; |
146 | 40 | } |
147 | | |
148 | | inline SqliteConnection::ConnectionType SqliteConnection::getConnectionType() const |
149 | 12 | { |
150 | 12 | return m_connectionType; |
151 | 12 | } |
152 | | |
153 | | inline sqlite3* SqliteConnection::getConnection() |
154 | 22 | { |
155 | 22 | return m_connection; |
156 | 22 | } |
157 | | |
158 | | inline void SqliteConnection::executeUpdate(StringView sqlQuery) |
159 | 17 | { |
160 | 17 | std::unique_ptr<sqlite3_stmt, SqliteFinalizer> statement(prepareStatement(sqlQuery)); |
161 | 17 | int result = sqlite3_step(statement.get()); |
162 | 17 | if (result != SQLITE_DONE) |
163 | 1 | { |
164 | 1 | throw SqliteException("SqliteConnection::executeUpdate(): sqlite3_step failed: ") << |
165 | 1 | SqliteErrorCode(result); |
166 | 1 | } |
167 | 17 | } |
168 | | |
169 | | inline sqlite3_stmt* SqliteConnection::prepareStatement(StringView sqlQuery) |
170 | 42 | { |
171 | 42 | sqlite3_stmt* statement = nullptr; |
172 | 42 | const int result = sqlite3_prepare_v2(m_connection, sqlQuery.data(), static_cast<int>(sqlQuery.size()), |
173 | 42 | &statement, nullptr); |
174 | 42 | if (result != SQLITE_OK) |
175 | 7 | { |
176 | 7 | throw SqliteException("SqliteConnection::prepareStatement(): sqlite3_prepare_v2() failed: ") << |
177 | 7 | SqliteErrorCode(result); |
178 | 7 | } |
179 | | |
180 | 35 | return statement; |
181 | 42 | } |
182 | | |
183 | | inline bool SqliteConnection::startTransaction() |
184 | 2 | { |
185 | 2 | bool wasTransactionStarted = false; |
186 | 2 | if (sqlite3_get_autocommit(m_connection) != 0) |
187 | 1 | { |
188 | 1 | executeUpdate("BEGIN;"); |
189 | 1 | wasTransactionStarted = true; |
190 | 1 | } |
191 | | |
192 | 2 | return wasTransactionStarted; |
193 | 2 | } |
194 | | |
195 | | inline void SqliteConnection::endTransaction(bool wasTransactionStarted) |
196 | 2 | { |
197 | 2 | if (wasTransactionStarted) |
198 | 1 | executeUpdate("COMMIT;"); |
199 | 2 | } |
200 | | |
201 | | } // namespace zserio |
202 | | |
203 | | #endif // ZSERIO_SQL_CONNECTION_H_INC |