Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2023 Carl Zeiss Microscopy GmbH | ||
2 | // | ||
3 | // SPDX-License-Identifier: MIT | ||
4 | |||
5 | #include "sqlite_DbStatement.h" | ||
6 | #include <exceptions.h> | ||
7 | #include <sstream> | ||
8 | |||
9 | using namespace std; | ||
10 | using namespace imgdoc2; | ||
11 | |||
12 | 110522 | SqliteDbStatement::SqliteDbStatement(sqlite3_stmt* sql_statement) : | |
13 | 110522 | sql_statement_(sql_statement) | |
14 | { | ||
15 | 110522 | } | |
16 | |||
17 | 221044 | /*virtual*/SqliteDbStatement::~SqliteDbStatement() | |
18 | { | ||
19 | // TODO(JBL): check error (-> https://www.sqlite.org/c3ref/finalize.html) | ||
20 | 221044 | sqlite3_finalize(this->sql_statement_); | |
21 | 221044 | } | |
22 | |||
23 | ✗ | /*virtual*/void SqliteDbStatement::Reset() | |
24 | { | ||
25 | ✗ | throw std::logic_error("not implemented"); | |
26 | } | ||
27 | |||
28 | 1776 | /*virtual*/void SqliteDbStatement::BindNull(int index) | |
29 | { | ||
30 | 1776 | const int return_code = sqlite3_bind_null(this->sql_statement_, index); | |
31 | 1776 | this->ThrowIfBindError(return_code, "sqlite3_bind_null"); | |
32 | 1776 | } | |
33 | |||
34 | 291276 | /*virtual*/void SqliteDbStatement::BindInt32(int index, std::int32_t value) | |
35 | { | ||
36 | 291276 | const int return_code = sqlite3_bind_int(this->sql_statement_, index, value); | |
37 | 291276 | this->ThrowIfBindError(return_code, "sqlite3_bind_int"); | |
38 | 291276 | } | |
39 | |||
40 | 67854 | /*virtual*/void SqliteDbStatement::BindInt64(int index, std::int64_t value) | |
41 | { | ||
42 | 67854 | const int return_code = sqlite3_bind_int64(this->sql_statement_, index, value); | |
43 | 67854 | this->ThrowIfBindError(return_code, "sqlite3_bind_int64"); | |
44 | 67854 | } | |
45 | |||
46 | 355622 | /*virtual*/void SqliteDbStatement::BindDouble(int index, double value) | |
47 | { | ||
48 | 355622 | const int return_code = sqlite3_bind_double(this->sql_statement_, index, value); | |
49 | 355622 | this->ThrowIfBindError(return_code, "sqlite3_bind_double"); | |
50 | 355622 | } | |
51 | |||
52 | 1434 | /*virtual*/void SqliteDbStatement::BindString(int index, const char* value) | |
53 | { | ||
54 | // -1 means: determine length (assuming it is a null-terminated string) | ||
55 | // SQLITE_TRANSIENT means: make a copy of the string | ||
56 | 1434 | const int return_code = sqlite3_bind_text(this->sql_statement_, index, value, -1, SQLITE_TRANSIENT); | |
57 | 1434 | this->ThrowIfBindError(return_code, "sqlite3_bind_text"); | |
58 | 1434 | } | |
59 | |||
60 | 758 | /*virtual*/void SqliteDbStatement::BindStringView(int index, const std::string_view& value) | |
61 | { | ||
62 | 758 | const int return_code = sqlite3_bind_text(this->sql_statement_, index, value.data(), value.size(), SQLITE_TRANSIENT); | |
63 | 758 | this->ThrowIfBindError(return_code, "sqlite3_bind_text"); | |
64 | 758 | } | |
65 | |||
66 | 8 | /*virtual*/void SqliteDbStatement::BindBlob_Static(int index, const void* data, size_t size) | |
67 | { | ||
68 | 8 | const int return_code = sqlite3_bind_blob64(this->sql_statement_, index, data, size, SQLITE_STATIC); | |
69 | 8 | this->ThrowIfBindError(return_code, "sqlite3_bind_blob64"); | |
70 | 8 | } | |
71 | |||
72 | 234596 | /*virtual*/sqlite3_stmt* SqliteDbStatement::GetSqliteSqlStatement() | |
73 | { | ||
74 | 234596 | return this->sql_statement_; | |
75 | } | ||
76 | |||
77 | 5718 | /*virtual*/std::int32_t SqliteDbStatement::GetResultInt32(int column) | |
78 | { | ||
79 | 5718 | const std::int32_t value = sqlite3_column_int(this->sql_statement_, column); | |
80 | 5718 | return value; | |
81 | } | ||
82 | |||
83 | 28 | /*virtual*/std::optional<std::int32_t> SqliteDbStatement::GetResultInt32OrNull(int column) | |
84 | { | ||
85 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | int32_t result = this->GetResultInt32(column); |
86 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 24 times.
|
28 | if (result == 0) |
87 | { | ||
88 | // a value of 0 **could** mean that we actually read a NULL (and it got coalesced into a 0) -> https://www.sqlite.org/c3ref/column_blob.html | ||
89 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | if (sqlite3_column_type(this->sql_statement_, column) == SQLITE_NULL) |
90 | { | ||
91 | 4 | return {}; | |
92 | } | ||
93 | } | ||
94 | |||
95 | 24 | return result; | |
96 | } | ||
97 | |||
98 | 7776 | /*virtual*/std::int64_t SqliteDbStatement::GetResultInt64(int column) | |
99 | { | ||
100 | 7776 | const int64_t value = sqlite3_column_int64(this->sql_statement_, column); | |
101 | 7776 | return value; | |
102 | } | ||
103 | |||
104 | 108 | /*virtual*/double SqliteDbStatement::GetResultDouble(int column) | |
105 | { | ||
106 | 108 | const double value = sqlite3_column_double(this->sql_statement_, column); | |
107 | 108 | return value; | |
108 | } | ||
109 | |||
110 | 64 | /*virtual*/std::optional<double> SqliteDbStatement::GetResultDoubleOrNull(int column) | |
111 | { | ||
112 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | const auto result = this->GetResultDouble(column); |
113 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 40 times.
|
64 | if (result == 0.0) |
114 | { | ||
115 | // a value of 0 **could** mean that we actually read a NULL (and it got coalesced into a 0) -> https://www.sqlite.org/c3ref/column_blob.html | ||
116 |
3/4✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 16 times.
|
24 | if (sqlite3_column_type(this->sql_statement_, column) == SQLITE_NULL) |
117 | { | ||
118 | 8 | return {}; | |
119 | } | ||
120 | } | ||
121 | |||
122 | 56 | return result; | |
123 | } | ||
124 | |||
125 | 20 | /*virtual*/std::uint32_t SqliteDbStatement::GetResultUInt32(int column) | |
126 | { | ||
127 | 20 | const uint32_t value = static_cast<uint32_t>(sqlite3_column_int(this->sql_statement_, column)); | |
128 | 20 | return value; | |
129 | } | ||
130 | |||
131 | 8 | /*virtual*/std::uint8_t SqliteDbStatement::GetResultUInt8(int column) | |
132 | { | ||
133 | 8 | const uint8_t value = static_cast<uint8_t>(sqlite3_column_int(this->sql_statement_, column)); | |
134 | 8 | return value; | |
135 | } | ||
136 | |||
137 | 4 | /*virtual*/void SqliteDbStatement::GetResultBlob(int column, imgdoc2::IBlobOutput* blobOutput) | |
138 | { | ||
139 | 4 | int size_of_blob = sqlite3_column_bytes(this->sql_statement_, column); | |
140 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | if (blobOutput->Reserve(size_of_blob)) |
141 | { | ||
142 | 4 | const void* ptr_data = sqlite3_column_blob(this->sql_statement_, column); | |
143 | 4 | blobOutput->SetData(0, size_of_blob, ptr_data); | |
144 | } | ||
145 | 4 | } | |
146 | |||
147 | 1250 | /*virtual*/std::string SqliteDbStatement::GetResultString(int column) | |
148 | { | ||
149 | // TODO(JBL): this method is creating a copy of the string, it might be a good idea to have a method | ||
150 | // which returns a "dangereous pointer" | ||
151 | 1250 | const unsigned char* str = sqlite3_column_text(this->sql_statement_, column); | |
152 | |||
153 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) no way (I can see) to prevent the reinterpret_cast here | ||
154 |
1/2✓ Branch 1 taken 1250 times.
✗ Branch 2 not taken.
|
3750 | return string{ reinterpret_cast<const char*>(str) }; |
155 | } | ||
156 | |||
157 | 718728 | void SqliteDbStatement::ThrowIfBindError(int error_code, const char* function_name) | |
158 | { | ||
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 718728 times.
|
718728 | if (error_code != SQLITE_OK) |
160 | { | ||
161 | // TODO(JBL) : not exactly sure which error to throw | ||
162 | // https://www.sqlite.org/c3ref/bind_blob.html | ||
163 | ✗ | ostringstream string_stream; | |
164 | ✗ | string_stream << "Error binding a value (with function \"" << function_name << "\")."; | |
165 | ✗ | throw database_exception(string_stream.str().c_str(), error_code); | |
166 | ✗ | } | |
167 | 718728 | } | |
168 |