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 "../sl3/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 : 118 : createStmt (sqlite3* db, const std::string& sql)
28 : : {
29 [ + + ]: 118 : if (db == nullptr)
30 [ + - ]: 8 : throw ErrNoConnection{};
31 : :
32 : 110 : sqlite3_stmt* stmt = nullptr;
33 : 110 : const char* unussedSQL = nullptr;
34 : :
35 [ + - ]: 110 : int rc = sqlite3_prepare_v2 (db, sql.c_str (), -1, &stmt, &unussedSQL);
36 : :
37 [ + + ]: 110 : if (rc != SQLITE_OK)
38 : : {
39 [ + - + - ]: 8 : SQLite3Error sl3error (rc, sqlite3_errmsg (db));
40 : 8 : throw sl3error;
41 : 8 : }
42 : :
43 : 102 : return stmt;
44 : : }
45 : :
46 : : DbValues
47 : 94 : createParameters (sqlite3_stmt* stmt)
48 : : {
49 : 94 : const size_t paracount = as_size_t (sqlite3_bind_parameter_count (stmt));
50 : :
51 : : using container = DbValues::container_type;
52 : :
53 [ + - + + : 124 : return paracount > 0 ? DbValues (container (paracount, Type::Variant))
+ + + + -
- - - -
- ]
54 [ + + ]: 203 : : DbValues ();
55 : : }
56 : :
57 : : void
58 : 97 : bind (sqlite3_stmt* stmt, DbValues& parameters)
59 : : {
60 : 97 : int curParaNr = 0;
61 [ + - + - : 156 : 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 : 97 : }
111 : :
112 : : } // ns
113 : :
114 : 109 : Command::Command (Connection connection, const std::string& sql)
115 : 109 : : _connection (std::move (connection))
116 [ + + ]: 109 : , _stmt (createStmt (_connection->db (), sql))
117 [ + - ]: 94 : , _parameters (createParameters (_stmt))
118 : : {
119 : 109 : }
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 : 99 : Command::~Command ()
145 : : {
146 [ + + ]: 99 : if (_stmt) // its not a moved from zombi
147 : : {
148 [ + - ]: 98 : if (_connection->isValid ()) // otherwise database will have done this
149 : : {
150 : 98 : sqlite3_finalize (_stmt);
151 : : }
152 : : }
153 : 99 : }
154 : :
155 : : Dataset
156 : 18 : Command::select ()
157 : : {
158 [ + - ]: 18 : 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 : 38 : Command::select (const DbValues& parameters, const Types& types)
169 : : {
170 [ + - + - ]: 38 : Dataset ds{types};
171 : 129 : Callback fillds = [&ds] (Columns columns) -> bool {
172 [ + + ]: 53 : if (ds._names.size () == 0)
173 : : {
174 : 38 : const int typeCount = static_cast<int> (ds._fieldtypes.size ());
175 : :
176 [ + + ]: 38 : if (typeCount == 0)
177 : : {
178 : : using container_type = Types::container_type;
179 [ + - + - ]: 18 : container_type c (as_size_t (columns.count ()), Type::Variant);
180 [ + - ]: 18 : Types fieldtypes{c};
181 : 18 : ds._fieldtypes.swap (fieldtypes);
182 : 18 : }
183 [ + + ]: 20 : else if (typeCount != columns.count ())
184 : : {
185 : : throw ErrTypeMisMatch (
186 [ + - ]: 1 : "DbValuesTypeList.size != queryrow.getColumnCount()");
187 : : }
188 [ + - ]: 37 : ds._names = columns.getNames ();
189 : : }
190 : :
191 : : // this will throw if a type does not match.
192 [ + + + - ]: 52 : ds._cont.emplace_back (columns.getRow (ds._fieldtypes));
193 : :
194 : 50 : return true;
195 : 38 : };
196 : :
197 [ + - + + ]: 41 : execute (fillds, parameters);
198 : 70 : return ds;
199 : 41 : }
200 : :
201 : : void
202 : 5 : Command::execute ()
203 : : {
204 [ + + ]: 5 : execute (DbValues ());
205 : 4 : }
206 : :
207 : : void
208 : 19 : Command::execute (const DbValues& parameters)
209 : : {
210 [ + + ]: 21 : execute ([] (Columns) -> bool { return true; }, parameters);
211 : 17 : }
212 : :
213 : : void
214 : 6 : Command::execute (RowCallback& cb, const DbValues& parameters)
215 : : {
216 [ + - ]: 6 : cb.onStart ();
217 : :
218 : 11 : auto cbf = [&cb] (Columns cols) -> bool { return cb.onRow (cols); };
219 : :
220 [ + - ]: 6 : execute (cbf, parameters);
221 : :
222 [ + - ]: 6 : cb.onEnd ();
223 : 6 : }
224 : :
225 : : void
226 : 98 : Command::execute (Callback callback, const DbValues& parameters)
227 : : {
228 [ + - ]: 98 : _connection->ensureValid ();
229 : :
230 [ + + ]: 98 : if (parameters.size () > 0)
231 [ + + ]: 15 : setParameters (parameters);
232 : :
233 [ + - ]: 97 : bind (_stmt, _parameters);
234 : :
235 : : // use this to ensure a reset of _stmt
236 : : using ResetGuard
237 : : = std::unique_ptr<sqlite3_stmt, decltype (&sqlite3_reset)>;
238 : 97 : ResetGuard resetGuard (_stmt, &sqlite3_reset);
239 : :
240 : 97 : bool loop = true;
241 [ + + ]: 265 : while (loop)
242 : : {
243 [ + - ]: 173 : int rc = sqlite3_step (_stmt);
244 : :
245 [ + + + ]: 173 : switch (rc)
246 : : {
247 : 68 : case SQLITE_OK:
248 : : case SQLITE_DONE:
249 : : {
250 : 68 : loop = false;
251 : 68 : break;
252 : : }
253 : 104 : case SQLITE_ROW:
254 : : {
255 [ + - + + ]: 104 : loop = callback (Columns{_stmt});
256 : 100 : break;
257 : : }
258 : :
259 : 1 : default:
260 : : {
261 [ + - ]: 1 : auto db = sqlite3_db_handle (_stmt);
262 [ + - + - ]: 1 : SQLite3Error sl3error (rc, sqlite3_errmsg (db));
263 : 1 : throw sl3error;
264 : 1 : }
265 : : }
266 : : }
267 : 97 : }
268 : :
269 : : DbValues&
270 : 2 : Command::getParameters ()
271 : : {
272 : 2 : return _parameters;
273 : : }
274 : :
275 : : const DbValues&
276 : 1 : Command::getParameters () const
277 : : {
278 : 1 : return _parameters;
279 : : }
280 : :
281 : : DbValue&
282 : 2 : Command::getParameter (int idx)
283 : : {
284 : 2 : return _parameters.at (as_size_t (idx));
285 : : }
286 : :
287 : : const DbValue&
288 : 2 : Command::getParameter (int idx) const
289 : : {
290 : 2 : return _parameters.at (as_size_t (idx));
291 : : }
292 : :
293 : : void
294 : 17 : Command::setParameters (const DbValues& values)
295 : : {
296 [ + + ]: 17 : if (values.size () != _parameters.size ())
297 : : {
298 [ + - ]: 2 : throw ErrTypeMisMatch ("parameter size incorrect");
299 : : }
300 : :
301 [ + + ]: 61 : for (size_t i = 0; i < values.size (); ++i)
302 : : {
303 : 47 : _parameters[i] = values[i];
304 : : }
305 : 14 : }
306 : :
307 : : void
308 : 3 : Command::resetParameters (DbValues values)
309 : : {
310 [ + + + - : 7 : ASSERT_EXCEPT (values.size () == _parameters.size (), ErrTypeMisMatch);
+ - + - +
- ]
311 : : // auto tmp = values;
312 : 1 : _parameters.swap (values);
313 : 1 : }
314 : :
315 : : std::vector<std::string>
316 : 3 : Command::getParameterNames () const
317 : : {
318 : 3 : std::vector<std::string> names;
319 [ + - ]: 3 : names.resize (_parameters.size ());
320 : :
321 [ + + ]: 11 : for (unsigned int i = 0; i < _parameters.size (); ++i)
322 : : {
323 : : const char* chrname
324 [ + - ]: 8 : = sqlite3_bind_parameter_name (_stmt, as_int (i + 1));
325 [ + - + - ]: 8 : names[i] = chrname ? chrname : "";
326 : : }
327 : 3 : return names;
328 : 0 : }
329 : :
330 : : } // ns
|