Branch data Line data Source code
1 : : /******************************************************************************
2 : : ------------- Copyright (c) 2009-2023 H a r a l d A c h i t z ---------------
3 : : ---------- < h a r a l d dot a c h i t z at g m a i l dot c o m > ------------
4 : : ---- This Source Code Form is subject to the terms of the Mozilla Public -----
5 : : ---- License, v. 2.0. If a copy of the MPL was not distributed with this -----
6 : : ---------- file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------
7 : : ******************************************************************************/
8 : :
9 : : #include <sl3/command.hpp>
10 : :
11 : : #include <functional>
12 : :
13 : : #include <sqlite3.h>
14 : :
15 : : #include "connection.hpp"
16 : : #include <sl3/columns.hpp>
17 : : #include <sl3/database.hpp>
18 : : #include <sl3/error.hpp>
19 : :
20 : : #include "utils.hpp"
21 : :
22 : : namespace sl3
23 : : {
24 : : namespace
25 : : {
26 : : sqlite3_stmt*
27 : 123 : createStmt (sqlite3* db, const std::string& sql)
28 : : {
29 : 123 : if (db == nullptr)
30 : 8 : throw ErrNoConnection{};
31 : :
32 : 115 : sqlite3_stmt* stmt = nullptr;
33 : 115 : const char* unussedSQL = nullptr;
34 : :
35 : 115 : int rc = sqlite3_prepare_v2 (db, sql.c_str (), -1, &stmt, &unussedSQL);
36 : :
37 : 115 : if (rc != SQLITE_OK)
38 : : {
39 : 8 : SQLite3Error sl3error (rc, sqlite3_errmsg (db));
40 : 8 : throw sl3error;
41 : 8 : }
42 : :
43 : 107 : return stmt;
44 : : }
45 : :
46 : : DbValues
47 : 99 : createParameters (sqlite3_stmt* stmt)
48 : : {
49 : 99 : const size_t paracount = as_size_t (sqlite3_bind_parameter_count (stmt));
50 : :
51 : : using container = DbValues::container_type;
52 : :
53 : 129 : return paracount > 0 ? DbValues (container (paracount, Type::Variant))
54 : 213 : : DbValues ();
55 : : }
56 : :
57 : : void
58 : 102 : bind (sqlite3_stmt* stmt, DbValues& parameters)
59 : : {
60 : 102 : int curParaNr = 0;
61 : 161 : for (auto& val : parameters)
62 : : {
63 : 59 : curParaNr += 1; // sqlite starts at 1
64 : :
65 : : int rc;
66 : :
67 : 59 : switch (val.type ())
68 : : {
69 : 27 : case Type::Int:
70 : 27 : rc = sqlite3_bind_int64 (stmt, curParaNr, val.getInt ());
71 : 27 : break;
72 : :
73 : 11 : case Type::Real:
74 : 11 : rc = sqlite3_bind_double (stmt, curParaNr, val.getReal ());
75 : 11 : break;
76 : :
77 : 13 : case Type::Text:
78 : : // note, i do not want \0 in the db so take size
79 : : // SQLITE_TRANSIENT would copy the string , is unwanted here
80 : 13 : rc = sqlite3_bind_text (
81 : : stmt,
82 : : curParaNr,
83 : 13 : val.getText ().c_str (),
84 : 13 : static_cast<int> (val.getText ().size ()),
85 : : SQLITE_STATIC);
86 : :
87 : 13 : break;
88 : :
89 : 4 : case Type::Blob:
90 : 4 : rc = sqlite3_bind_blob (
91 : : stmt,
92 : : curParaNr,
93 : 4 : &(val.getBlob ()[0]),
94 : 4 : static_cast<int> (val.getBlob ().size ()),
95 : : SQLITE_STATIC);
96 : :
97 : 4 : break;
98 : :
99 : 4 : case Type::Null:
100 : 4 : rc = sqlite3_bind_null (stmt, curParaNr);
101 : 4 : break;
102 : :
103 : 0 : default:
104 : : throw ErrUnexpected (); // LCOV_EXCL_LINE
105 : : }
106 : :
107 : 59 : if (rc != SQLITE_OK)
108 : : throw sl3::SQLite3Error (rc, ""); // LCOV_EXCL_LINE TODO ho to test
109 : : }
110 : 102 : }
111 : :
112 : : } // ns
113 : :
114 : 114 : Command::Command (Connection connection, const std::string& sql)
115 : 114 : : _connection (std::move (connection))
116 : 114 : , _stmt (createStmt (_connection->db (), sql))
117 : 99 : , _parameters (createParameters (_stmt))
118 : : {
119 : 114 : }
120 : :
121 : 9 : Command::Command (Connection connection,
122 : : const std::string& sql,
123 : 9 : DbValues parameters)
124 : 9 : : _connection (std::move (connection))
125 : 9 : , _stmt (createStmt (_connection->db (), sql))
126 : 8 : , _parameters (std::move (parameters))
127 : : {
128 : 8 : const size_t paracount = as_size_t (sqlite3_bind_parameter_count (_stmt));
129 : :
130 : 8 : if (paracount != _parameters.size ())
131 : : {
132 : 4 : throw ErrTypeMisMatch ("Incorrect parameter count");
133 : : }
134 : 13 : }
135 : :
136 : 1 : Command::Command (Command&& other)
137 : 1 : : _connection (std::move (other._connection))
138 : 1 : , _stmt (other._stmt)
139 : 1 : , _parameters (std::move (other._parameters))
140 : : { // clear stm so that d'tor ot other does no action
141 : 1 : other._stmt = nullptr;
142 : 1 : }
143 : :
144 : 104 : Command::~Command ()
145 : : {
146 : 104 : if (_stmt) // its not a moved from zombi
147 : : {
148 : 103 : if (_connection->isValid ()) // otherwise database will have done this
149 : : {
150 : 103 : sqlite3_finalize (_stmt);
151 : : }
152 : : }
153 : 104 : }
154 : :
155 : : Dataset
156 : 22 : Command::select ()
157 : : {
158 : 22 : return select (DbValues (), Types ());
159 : : }
160 : :
161 : : Dataset
162 : 20 : Command::select (const Types& types, const DbValues& parameters)
163 : : {
164 : 20 : return select (parameters, types);
165 : : }
166 : :
167 : : Dataset
168 : 42 : Command::select (const DbValues& parameters, const Types& types)
169 : : {
170 : 42 : Dataset ds{types};
171 : 142 : Callback fillds = [&ds] (Columns columns) -> bool
172 : : {
173 : 58 : if (ds._names.size () == 0)
174 : : {
175 : 42 : const int typeCount = static_cast<int> (ds._fieldtypes.size ());
176 : :
177 : 42 : if (typeCount == 0)
178 : : {
179 : : using container_type = Types::container_type;
180 : 22 : container_type c (as_size_t (columns.count ()), Type::Variant);
181 : 22 : Types fieldtypes{c};
182 : 22 : ds._fieldtypes.swap (fieldtypes);
183 : 22 : }
184 : 20 : else if (typeCount != columns.count ())
185 : : {
186 : : throw ErrTypeMisMatch (
187 : 1 : "DbValuesTypeList.size != queryrow.getColumnCount()");
188 : : }
189 : 41 : ds._names = columns.getNames ();
190 : : }
191 : :
192 : : // this will throw if a type does not match.
193 : 57 : ds._cont.emplace_back (columns.getRow (ds._fieldtypes));
194 : :
195 : 55 : return true;
196 : 42 : };
197 : :
198 : 45 : execute (fillds, parameters);
199 : 78 : return ds;
200 : 45 : }
201 : :
202 : : void
203 : 5 : Command::execute ()
204 : : {
205 : 5 : execute (DbValues ());
206 : 4 : }
207 : :
208 : : void
209 : 19 : Command::execute (const DbValues& parameters)
210 : : {
211 : 21 : execute ([] (Columns) -> bool { return true; }, parameters);
212 : 17 : }
213 : :
214 : : void
215 : 6 : Command::execute (RowCallback& cb, const DbValues& parameters)
216 : : {
217 : 6 : cb.onStart ();
218 : :
219 : 11 : auto cbf = [&cb] (Columns cols) -> bool { return cb.onRow (cols); };
220 : :
221 : 6 : execute (cbf, parameters);
222 : :
223 : 6 : cb.onEnd ();
224 : 6 : }
225 : :
226 : : void
227 : 103 : Command::execute (Callback callback, const DbValues& parameters)
228 : : {
229 : 103 : _connection->ensureValid ();
230 : :
231 : 103 : if (parameters.size () > 0)
232 : 15 : setParameters (parameters);
233 : :
234 : 102 : bind (_stmt, _parameters);
235 : :
236 : : // use this to ensure a reset of _stmt
237 : : using ResetGuard
238 : : = std::unique_ptr<sqlite3_stmt, decltype (&sqlite3_reset)>;
239 : 102 : ResetGuard resetGuard (_stmt, &sqlite3_reset);
240 : :
241 : 102 : bool loop = true;
242 : 280 : while (loop)
243 : : {
244 : 183 : int rc = sqlite3_step (_stmt);
245 : :
246 : 183 : switch (rc)
247 : : {
248 : 72 : case SQLITE_OK:
249 : : case SQLITE_DONE:
250 : : {
251 : 72 : loop = false;
252 : 72 : break;
253 : : }
254 : 110 : case SQLITE_ROW:
255 : : {
256 : 110 : loop = callback (Columns{_stmt});
257 : 106 : break;
258 : : }
259 : :
260 : 1 : default:
261 : : {
262 : 1 : auto db = sqlite3_db_handle (_stmt);
263 : 1 : SQLite3Error sl3error (rc, sqlite3_errmsg (db));
264 : 1 : throw sl3error;
265 : 1 : }
266 : : }
267 : : }
268 : 102 : }
269 : :
270 : : DbValues&
271 : 2 : Command::getParameters ()
272 : : {
273 : 2 : return _parameters;
274 : : }
275 : :
276 : : const DbValues&
277 : 1 : Command::getParameters () const
278 : : {
279 : 1 : return _parameters;
280 : : }
281 : :
282 : : DbValue&
283 : 2 : Command::getParameter (int idx)
284 : : {
285 : 2 : return _parameters.at (as_size_t (idx));
286 : : }
287 : :
288 : : const DbValue&
289 : 2 : Command::getParameter (int idx) const
290 : : {
291 : 2 : return _parameters.at (as_size_t (idx));
292 : : }
293 : :
294 : : void
295 : 17 : Command::setParameters (const DbValues& values)
296 : : {
297 : 17 : if (values.size () != _parameters.size ())
298 : : {
299 : 2 : throw ErrTypeMisMatch ("parameter size incorrect");
300 : : }
301 : :
302 : 61 : for (size_t i = 0; i < values.size (); ++i)
303 : : {
304 : 47 : _parameters[i] = values[i];
305 : : }
306 : 14 : }
307 : :
308 : : void
309 : 3 : Command::resetParameters (DbValues values)
310 : : {
311 : 7 : ASSERT_EXCEPT (values.size () == _parameters.size (), ErrTypeMisMatch);
312 : : // auto tmp = values;
313 : 1 : _parameters.swap (values);
314 : 1 : }
315 : :
316 : : std::vector<std::string>
317 : 3 : Command::getParameterNames () const
318 : : {
319 : 3 : std::vector<std::string> names;
320 : 3 : names.resize (_parameters.size ());
321 : :
322 : 11 : for (unsigned int i = 0; i < _parameters.size (); ++i)
323 : : {
324 : : const char* chrname
325 : 8 : = sqlite3_bind_parameter_name (_stmt, as_int (i + 1));
326 : 8 : names[i] = chrname ? chrname : "";
327 : : }
328 : 3 : return names;
329 : 0 : }
330 : :
331 : : } // ns
|