Audacity 3.2.0
Statement.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 * SPDX-FileName: Statement.cpp
4 * SPDX-FileContributor: Dmitry Vedenko
5 */
6
7#include "Statement.h"
8
9#include <cassert>
10#include <cstring>
11#include <utility>
12
13#include "Connection.h"
14
15#include "sqlite3.h"
16
17namespace audacity::sqlite
18{
19struct StatementHandle final
20{
21 sqlite3_stmt* Handle {};
22
23 explicit StatementHandle(sqlite3_stmt* Handle) noexcept
24 : Handle { Handle }
25 {
26 }
27
28 operator sqlite3_stmt* () noexcept
29 {
30 return Handle;
31 }
32
34 {
35 if (Handle != nullptr)
36 sqlite3_finalize(Handle);
37 }
38};
39
40Statement::Statement(sqlite3_stmt* stmt)
41 : mStatement { std::make_shared<StatementHandle>(stmt) }
42{
43}
44
46{
47 *this = std::move(rhs);
48}
49
51{
52 std::swap(mStatement, rhs.mStatement);
53 return *this;
54}
55
57{
59 return *mRunContext;
60}
61
63 : mStatement { std::move(stmt) }
64{
65}
66
68{
69 *this = std::move(rhs);
70}
71
73{
74 std::swap(mStatement, rhs.mStatement);
75 std::swap(mErrors, rhs.mErrors);
76
77 return *this;
78}
79
80template<typename Binder>
82{
83 if (mStatement == nullptr)
84 mErrors.emplace_back(Error(SQLITE_MISUSE));
85 else if (int result = binder(); result != SQLITE_OK)
86 mErrors.emplace_back(Error(result));
87
88 return *this;
89}
90
92 int index, const void* data, int64_t size, bool makeCopy)
93{
94 if (data == nullptr)
95 return BindZeroBlob(index, size);
96
97 return DoBind(
98 [&]
99 {
100 return sqlite3_bind_blob64(
101 *mStatement, index, data, size,
102 makeCopy ? SQLITE_TRANSIENT : SQLITE_STATIC);
103 });
104}
105
106
108 int index, const std::string& value, bool makeCopy)
109{
110 return Bind(index, std::string_view { value }, makeCopy);
111}
112
114 int index, std::string_view value, bool makeCopy)
115{
116 return DoBind(
117 [&]
118 {
119 if (mNeedsReset)
120 {
121 mNeedsReset = false;
122 sqlite3_reset(*mStatement);
123 }
124
125 return sqlite3_bind_text(
126 *mStatement, index, value.data(), value.size(),
127 makeCopy ? SQLITE_TRANSIENT : SQLITE_STATIC);
128 });
129}
130
132 int index, const char* value, bool makeCopy)
133{
134 return Bind(index, std::string_view { value }, makeCopy);
135}
136
137RunContext& RunContext::Bind(int index, bool value)
138{
139 return Bind(index, static_cast<long long>(value));
140}
141
143RunContext::Bind(int index, int value)
144{
145 return Bind(index, static_cast<long long>(value));
146}
147
149RunContext::Bind(int index, long value)
150{
151 return Bind(index, static_cast<long long>(value));
152}
153
155RunContext::Bind(int index, long long value)
156{
157 return DoBind([&] { return sqlite3_bind_int64(*mStatement, index, value); });
158}
159
160RunContext& sqlite::RunContext::Bind(int index, std::size_t value)
161{
162 return DoBind([&] { return sqlite3_bind_int64(*mStatement, index, value); });
163}
164
166RunContext::Bind(int index, float value)
167{
168 return Bind(index, static_cast<double>(value));
169}
170
172RunContext::Bind(int index, double value)
173{
174 return DoBind([&] { return sqlite3_bind_double(*mStatement, index, value); });
175}
176
178RunContext::Bind(int index, std::nullptr_t)
179{
180 return DoBind([&] { return sqlite3_bind_null(*mStatement, index); });
181}
182
185{
186 return DoBind([&]
187 { return sqlite3_bind_zeroblob64(*mStatement, index, size); });
188}
189
190int RunContext::GetParameterIndex(const std::string& name) const noexcept
191{
192 return mStatement != nullptr ?
193 sqlite3_bind_parameter_index(*mStatement, name.data()) :
194 -1;
195}
196
198{
199 mNeedsReset = true;
200 return RunResult { mStatement, std::move(mErrors) };
201}
202
203RunResult::RunResult(StatementHandlePtr stmt, std::vector<Error> errors) noexcept
204 : mStatement { std::move(stmt) }
205 , mErrors { std::move(errors) }
206{
207 // mStatement can't be nullptr here, by construction
208 assert(mStatement != nullptr);
209
210 const auto rc = sqlite3_step(*mStatement);
211
212 mHasRows = rc == SQLITE_ROW;
213
214 if (rc == SQLITE_DONE)
215 mModifiedRowsCount = sqlite3_changes(sqlite3_db_handle(*mStatement));
216
217 if (rc != SQLITE_DONE && !mHasRows)
218 mErrors.emplace_back(Error(rc));
219}
220
222{
223 *this = std::move(rhs);
224}
225
227{
228 std::swap(mStatement, rhs.mStatement);
229 std::swap(mErrors, rhs.mErrors);
230 std::swap(mHasRows, rhs.mHasRows);
231 std::swap(mModifiedRowsCount, rhs.mModifiedRowsCount);
232
233 return *this;
234}
235
237{
238 if (mStatement != nullptr)
239 sqlite3_reset(*mStatement);
240}
241
242bool RunResult::IsOk() const noexcept
243{
244 return mErrors.empty();
245}
246
247bool RunResult::HasRows() const noexcept
248{
249 return mHasRows;
250}
251
253{
254 return mModifiedRowsCount;
255}
256
257const std::vector<Error>& RunResult::GetErrors() const noexcept
258{
259 return mErrors;
260}
261
263{
265}
266
268{
269 return RowIterator {};
270}
271
272RowIterator::RowIterator(StatementHandlePtr stmt, std::vector<Error>& errors) noexcept
273 : mStatement { std::move(stmt) }
274 , mErrors { &errors }
275{
276 // mStatement can't be nullptr here, by construction
277 assert(mStatement != nullptr);
278}
279
281 : mDone { true }
282{
283}
284
286{
287 *this = std::move(rhs);
288}
289
291{
292 std::swap(mStatement, rhs.mStatement);
293 std::swap(mErrors, rhs.mErrors);
294 std::swap(mRowIndex, rhs.mRowIndex);
295 std::swap(mDone, rhs.mDone);
296 return *this;
297}
298
300{
301 if (mStatement == nullptr || mDone)
302 return *this;
303
304 const auto rc = sqlite3_step(*mStatement);
305
306 if (rc == SQLITE_ROW)
307 ++mRowIndex;
308 else
309 {
310 mDone = true;
311
312 if (rc != SQLITE_DONE)
313 mErrors->emplace_back(Error(rc));
314 }
315
316 return *this;
317}
318
319bool RowIterator::operator==(const RowIterator& rhs) const noexcept
320{
321 if (mDone != rhs.mDone)
322 return false;
323
324 if (mDone)
325 return true;
326
327 return mStatement == rhs.mStatement && mRowIndex == rhs.mRowIndex;
328}
329
330bool RowIterator::operator!=(const RowIterator& rhs) const noexcept
331{
332 return !(*this == rhs);
333}
334
336{
337 return Row { mStatement, *mErrors };
338}
339
340Row::Row(StatementHandlePtr statement, std::vector<Error>& errors) noexcept
341 : mStatement { std::move(statement) }
342 , mErrors { &errors }
343{
344 if (mStatement != nullptr)
345 mColumnsCount = sqlite3_column_count(*mStatement);
346}
347
348template <typename Reader>
349bool Row::DoGet(Reader reader, int columnIndex) const
350{
351 if (mStatement == nullptr)
352 {
353 if (mErrors != nullptr)
354 mErrors->emplace_back(Error(SQLITE_MISUSE));
355 return false;
356 }
357
358 if (columnIndex < 0 || columnIndex >= mColumnsCount)
359 {
360 if (mErrors != nullptr)
361 mErrors->emplace_back(Error(SQLITE_RANGE));
362 return false;
363 }
364
365 if constexpr (std::is_void_v<decltype(reader())>)
366 {
367 reader();
368 return true;
369 }
370 else
371 return reader();
372}
373
374bool Row::Get(int columnIndex, bool& value) const
375{
376 return DoGet([&] { value = sqlite3_column_int(*mStatement, columnIndex) != 0; }, columnIndex);
377}
378
379bool Row::Get(int columnIndex, int& value) const
380{
381 return DoGet([&] { value = sqlite3_column_int(*mStatement, columnIndex); }, columnIndex);
382}
383
384bool Row::Get(int columnIndex, long& value) const
385{
386 if (sizeof(long) == 4)
387 return DoGet([&] { value = sqlite3_column_int(*mStatement, columnIndex); }, columnIndex);
388 else
389 return DoGet([&] { value = sqlite3_column_int64(*mStatement, columnIndex); }, columnIndex);
390}
391
392bool Row::Get(int columnIndex, long long& value) const
393{
394 return DoGet([&] { value = sqlite3_column_int64(*mStatement, columnIndex); }, columnIndex);
395}
396
397bool Row::Get(int columnIndex, float& value) const
398{
399 return DoGet(
400 [&]
401 {
402 value =
403 static_cast<float>(sqlite3_column_double(*mStatement, columnIndex));
404 },
405 columnIndex);
406}
407
408bool Row::Get(int columnIndex, double& value) const
409{
410 return DoGet([&] { value = sqlite3_column_double(*mStatement, columnIndex); },
411 columnIndex);
412}
413
414bool Row::Get(int columnIndex, std::string& value) const
415{
416 return DoGet(
417 [&]
418 {
419 const auto* text = reinterpret_cast<const char*>(
420 sqlite3_column_text(*mStatement, columnIndex));
421
422 if (text == nullptr)
423 return false;
424
425 // Per sqlite convention, text is never nullptr
426 value = text;
427 return true;
428 },
429 columnIndex);
430}
431
433{
434 return sqlite3_column_count(*mStatement);
435}
436
437int64_t Row::GetColumnBytes(int columnIndex) const
438{
439 return sqlite3_column_bytes(*mStatement, columnIndex);
440}
441
442int64_t Row::ReadData(int columnIndex, void* buffer, int64_t maxSize) const
443{
444 const auto* data = sqlite3_column_blob(*mStatement, columnIndex);
445
446 if (data == nullptr)
447 return 0;
448
449 const auto size = std::min(maxSize, GetColumnBytes(columnIndex));
450
451 std::memcpy(buffer, data, size);
452
453 return size;
454}
455
456} // namespace audacity::sqlite
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
A class representing an error in SQLite.
Definition: Error.h:17
A class representing a row in a result set.
Definition: Statement.h:32
StatementHandlePtr mStatement
Definition: Statement.h:62
int GetColumnCount() const
Definition: Statement.cpp:432
std::vector< Error > * mErrors
Definition: Statement.h:63
int64_t GetColumnBytes(int columnIndex) const
Definition: Statement.cpp:437
bool DoGet(Reader reader, int columnIndex) const
Definition: Statement.cpp:349
int64_t ReadData(int columnIndex, void *buffer, int64_t maxSize) const
Definition: Statement.cpp:442
bool Get(int columnIndex, bool &value) const
Definition: Statement.cpp:374
A class representing an iterator over a result set.
Definition: Statement.h:69
RowIterator & operator=(const RowIterator &)=delete
bool operator==(const RowIterator &other) const noexcept
Definition: Statement.cpp:319
Row operator*() const noexcept
Definition: Statement.cpp:335
StatementHandlePtr mStatement
Definition: Statement.h:90
std::vector< Error > * mErrors
Definition: Statement.h:91
RowIterator & operator++() noexcept
Definition: Statement.cpp:299
bool operator!=(const RowIterator &other) const noexcept
Definition: Statement.cpp:330
A class representing a context of a run operation.
Definition: Statement.h:133
RunContext & DoBind(Binder binder)
Definition: Statement.cpp:81
int GetParameterIndex(const std::string &name) const noexcept
Definition: Statement.cpp:190
std::vector< Error > mErrors
Definition: Statement.h:188
RunContext & Bind(int index, const void *data, int64_t size, bool makeCopy=true)
Definition: Statement.cpp:91
RunContext & operator=(const RunContext &)=delete
RunContext(StatementHandlePtr statement) noexcept
Definition: Statement.cpp:62
StatementHandlePtr mStatement
Definition: Statement.h:187
RunContext & BindZeroBlob(int index, int64_t size)
Definition: Statement.cpp:184
A class representing a result of a run operation.
Definition: Statement.h:99
const std::vector< Error > & GetErrors() const noexcept
Definition: Statement.cpp:257
RowIterator begin() noexcept
Definition: Statement.cpp:262
RunResult(StatementHandlePtr statement, std::vector< Error > errors) noexcept
Definition: Statement.cpp:203
bool IsOk() const noexcept
Definition: Statement.cpp:242
int GetModifiedRowsCount() const noexcept
Definition: Statement.cpp:252
std::vector< Error > mErrors
Definition: Statement.h:123
RowIterator end() noexcept
Definition: Statement.cpp:267
bool HasRows() const noexcept
Definition: Statement.cpp:247
RunResult & operator=(const RunResult &)=delete
StatementHandlePtr mStatement
Definition: Statement.h:122
A class representing a compiled statement.
Definition: Statement.h:202
Statement(sqlite3_stmt *stmt)
Definition: Statement.cpp:40
RunContext & Prepare() noexcept
Definition: Statement.cpp:56
Statement & operator=(const Statement &)=delete
StatementHandlePtr mStatement
Definition: Statement.h:222
std::optional< RunContext > mRunContext
Definition: Statement.h:223
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628
std::shared_ptr< StatementHandle > StatementHandlePtr
Definition: Statement.h:23
STL namespace.
StatementHandle(sqlite3_stmt *Handle) noexcept
Definition: Statement.cpp:23