src/zserio/SqliteConnection.h
Line | Count | Source |
1 | | #ifndef ZSERIO_SQL_CONNECTION_H_INC |
2 | | #define ZSERIO_SQL_CONNECTION_H_INC |
3 | | |
4 | | #include <memory> |
5 | | |
6 | | #include "zserio/SqliteException.h" |
7 | | #include "zserio/SqliteFinalizer.h" |
8 | | #include "zserio/StringView.h" |
9 | | |
10 | | #include "sqlite3.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( |
39 | | sqlite3* connection = nullptr, 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), |
131 | | m_connectionType(connectionType) |
132 | 20 | {} |
133 | | |
134 | | inline SqliteConnection::~SqliteConnection() |
135 | 20 | { |
136 | 20 | reset(); |
137 | 20 | } |
138 | | |
139 | | inline void SqliteConnection::reset(sqlite3* connection, ConnectionType connectionType) |
140 | 40 | { |
141 | | // close connection only if it is internal |
142 | 40 | if (m_connectionType == INTERNAL_CONNECTION) |
143 | 35 | { |
144 | 35 | sqlite3_close_v2(m_connection); // sqlite3_close_v2(NULL) is a harmless no-op |
145 | 35 | } |
146 | | |
147 | 40 | m_connection = connection; |
148 | 40 | m_connectionType = connectionType; |
149 | 40 | } |
150 | | |
151 | | inline SqliteConnection::ConnectionType SqliteConnection::getConnectionType() const |
152 | 12 | { |
153 | 12 | return m_connectionType; |
154 | 12 | } |
155 | | |
156 | | inline sqlite3* SqliteConnection::getConnection() |
157 | 22 | { |
158 | 22 | return m_connection; |
159 | 22 | } |
160 | | |
161 | | inline void SqliteConnection::executeUpdate(StringView sqlQuery) |
162 | 17 | { |
163 | 17 | std::unique_ptr<sqlite3_stmt, SqliteFinalizer> statement(prepareStatement(sqlQuery)); |
164 | 17 | int result = sqlite3_step(statement.get()); |
165 | 17 | if (result != SQLITE_DONE) |
166 | 1 | { |
167 | 1 | throw SqliteException("SqliteConnection::executeUpdate(): sqlite3_step failed: ") |
168 | 1 | << SqliteErrorCode(result); |
169 | 1 | } |
170 | 17 | } |
171 | | |
172 | | inline sqlite3_stmt* SqliteConnection::prepareStatement(StringView sqlQuery) |
173 | 42 | { |
174 | 42 | sqlite3_stmt* statement = nullptr; |
175 | 42 | const int result = sqlite3_prepare_v2( |
176 | 42 | m_connection, sqlQuery.data(), static_cast<int>(sqlQuery.size()), &statement, nullptr); |
177 | 42 | if (result != SQLITE_OK) |
178 | 7 | { |
179 | 7 | throw SqliteException("SqliteConnection::prepareStatement(): sqlite3_prepare_v2() failed: ") |
180 | 7 | << SqliteErrorCode(result); |
181 | 7 | } |
182 | | |
183 | 35 | return statement; |
184 | 42 | } |
185 | | |
186 | | inline bool SqliteConnection::startTransaction() |
187 | 2 | { |
188 | 2 | bool wasTransactionStarted = false; |
189 | 2 | if (sqlite3_get_autocommit(m_connection) != 0) |
190 | 1 | { |
191 | 1 | executeUpdate("BEGIN;"); |
192 | 1 | wasTransactionStarted = true; |
193 | 1 | } |
194 | | |
195 | 2 | return wasTransactionStarted; |
196 | 2 | } |
197 | | |
198 | | inline void SqliteConnection::endTransaction(bool wasTransactionStarted) |
199 | 2 | { |
200 | 2 | if (wasTransactionStarted) |
201 | 1 | { |
202 | 1 | executeUpdate("COMMIT;"); |
203 | 1 | } |
204 | 2 | } |
205 | | |
206 | | } // namespace zserio |
207 | | |
208 | | #endif // ZSERIO_SQL_CONNECTION_H_INC |