1 | // |
2 | // SQLiteStatementImpl.cpp |
3 | // |
4 | // Library: SQL/SQLite |
5 | // Package: SQLite |
6 | // Module: SQLiteStatementImpl |
7 | // |
8 | // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/SQL/SQLite/SQLiteStatementImpl.h" |
16 | #include "Poco/SQL/SQLite/Utility.h" |
17 | #include "Poco/SQL/SQLite/SQLiteException.h" |
18 | #include "Poco/String.h" |
19 | #include <cstdlib> |
20 | #include <cstring> |
21 | #if defined(POCO_UNBUNDLED) |
22 | #include <sqlite3.h> |
23 | #else |
24 | #include "sqlite3.h" |
25 | #endif |
26 | |
27 | |
28 | namespace Poco { |
29 | namespace SQL { |
30 | namespace SQLite { |
31 | |
32 | |
33 | const int SQLiteStatementImpl::POCO_SQLITE_INV_ROW_CNT = -1; |
34 | |
35 | |
36 | SQLiteStatementImpl::SQLiteStatementImpl(Poco::SQL::SessionImpl& rSession, sqlite3* pDB): |
37 | StatementImpl(rSession), |
38 | _pDB(pDB), |
39 | _pStmt(0), |
40 | _stepCalled(false), |
41 | _nextResponse(0), |
42 | _affectedRowCount(POCO_SQLITE_INV_ROW_CNT), |
43 | _canBind(false), |
44 | _isExtracted(false), |
45 | _canCompile(true) |
46 | { |
47 | _columns.resize(1); |
48 | } |
49 | |
50 | |
51 | SQLiteStatementImpl::~SQLiteStatementImpl() |
52 | { |
53 | try |
54 | { |
55 | clear(); |
56 | } |
57 | catch (...) |
58 | { |
59 | poco_unexpected(); |
60 | } |
61 | } |
62 | |
63 | |
64 | void SQLiteStatementImpl::compileImpl() |
65 | { |
66 | if (!_pLeftover) |
67 | { |
68 | _bindBegin = bindings().begin(); |
69 | } |
70 | |
71 | std::string statement(toString()); |
72 | |
73 | sqlite3_stmt* pStmt = 0; |
74 | const char* pSql = _pLeftover ? _pLeftover->c_str() : statement.c_str(); |
75 | |
76 | if (0 == std::strlen(pSql)) |
77 | throw InvalidSQLStatementException("Empty statements are illegal" ); |
78 | |
79 | int rc = SQLITE_OK; |
80 | const char* pLeftover = 0; |
81 | bool queryFound = false; |
82 | |
83 | do |
84 | { |
85 | rc = sqlite3_prepare_v2(_pDB, pSql, -1, &pStmt, &pLeftover); |
86 | if (rc != SQLITE_OK) |
87 | { |
88 | if (pStmt) sqlite3_finalize(pStmt); |
89 | pStmt = 0; |
90 | std::string errMsg = sqlite3_errmsg(_pDB); |
91 | Utility::throwException(_pDB, rc, errMsg); |
92 | } |
93 | else if (rc == SQLITE_OK && pStmt) |
94 | { |
95 | queryFound = true; |
96 | } |
97 | else if (rc == SQLITE_OK && !pStmt) // comment/whitespace ignore |
98 | { |
99 | pSql = pLeftover; |
100 | if (std::strlen(pSql) == 0) |
101 | { |
102 | // empty statement or an conditional statement! like CREATE IF NOT EXISTS |
103 | // this is valid |
104 | queryFound = true; |
105 | } |
106 | } |
107 | } while (rc == SQLITE_OK && !pStmt && !queryFound); |
108 | |
109 | //Finalization call in clear() invalidates the pointer, so the value is remembered here. |
110 | //For last statement in a batch (or a single statement), pLeftover == "", so the next call |
111 | // to compileImpl() shall return false immediately when there are no more statements left. |
112 | std::string leftOver(pLeftover); |
113 | trimInPlace(leftOver); |
114 | clear(); |
115 | _pStmt = pStmt; |
116 | if (!leftOver.empty()) |
117 | { |
118 | _pLeftover = new std::string(leftOver); |
119 | _canCompile = true; |
120 | } |
121 | else _canCompile = false; |
122 | |
123 | _pBinder = new Binder(_pStmt); |
124 | _pExtractor = new Extractor(_pStmt); |
125 | |
126 | if (SQLITE_DONE == _nextResponse && _isExtracted) |
127 | { |
128 | //if this is not the first compile and there has already been extraction |
129 | //during previous step, switch to the next set if there is one provided |
130 | if (hasMoreDataSets()) |
131 | { |
132 | activateNextDataSet(); |
133 | _isExtracted = false; |
134 | } |
135 | } |
136 | |
137 | int colCount = sqlite3_column_count(_pStmt); |
138 | |
139 | if (colCount) |
140 | { |
141 | std::size_t curDataSet = currentDataSet(); |
142 | if (curDataSet >= _columns.size()) _columns.resize(curDataSet + 1); |
143 | for (int i = 0; i < colCount; ++i) |
144 | { |
145 | MetaColumn mc(i, sqlite3_column_name(_pStmt, i), Utility::getColumnType(_pStmt, i)); |
146 | _columns[curDataSet].push_back(mc); |
147 | } |
148 | } |
149 | } |
150 | |
151 | |
152 | void SQLiteStatementImpl::bindImpl() |
153 | { |
154 | _stepCalled = false; |
155 | _nextResponse = 0; |
156 | if (_pStmt == 0) return; |
157 | |
158 | sqlite3_reset(_pStmt); |
159 | |
160 | int paramCount = sqlite3_bind_parameter_count(_pStmt); |
161 | if (0 == paramCount) |
162 | { |
163 | _canBind = false; |
164 | return; |
165 | } |
166 | |
167 | BindIt bindEnd = bindings().end(); |
168 | std::size_t availableCount = 0; |
169 | Bindings::difference_type bindCount = 0; |
170 | Bindings::iterator it = _bindBegin; |
171 | for (; it != bindEnd; ++it) |
172 | { |
173 | availableCount += (*it)->numOfColumnsHandled(); |
174 | if (availableCount <= paramCount) ++bindCount; |
175 | else break; |
176 | } |
177 | |
178 | if (availableCount < paramCount) |
179 | throw ParameterCountMismatchException(); |
180 | |
181 | Bindings::difference_type remainingBindCount = bindEnd - _bindBegin; |
182 | if (bindCount < remainingBindCount) |
183 | { |
184 | bindEnd = _bindBegin + bindCount; |
185 | _canBind = true; |
186 | } |
187 | else if (bindCount > remainingBindCount) |
188 | throw ParameterCountMismatchException(); |
189 | |
190 | std::size_t boundRowCount; |
191 | if (_bindBegin != bindings().end()) |
192 | { |
193 | boundRowCount = (*_bindBegin)->numOfRowsHandled(); |
194 | |
195 | Bindings::iterator oldBegin = _bindBegin; |
196 | for (std::size_t pos = 1; _bindBegin != bindEnd && (*_bindBegin)->canBind(); ++_bindBegin) |
197 | { |
198 | if (boundRowCount != (*_bindBegin)->numOfRowsHandled()) |
199 | throw BindingException("Size mismatch in Bindings. All Bindings MUST have the same size" ); |
200 | |
201 | std::size_t namedBindPos = 0; |
202 | if (!(*_bindBegin)->name().empty()) |
203 | namedBindPos = (std::size_t)sqlite3_bind_parameter_index(_pStmt, (*_bindBegin)->name().c_str()); |
204 | |
205 | (*_bindBegin)->bind((namedBindPos != 0) ? namedBindPos : pos); |
206 | pos += (*_bindBegin)->numOfColumnsHandled(); |
207 | } |
208 | |
209 | if ((*oldBegin)->canBind()) |
210 | { |
211 | //container binding will come back for more, so we must rewind |
212 | _bindBegin = oldBegin; |
213 | _canBind = true; |
214 | } |
215 | else _canBind = false; |
216 | } |
217 | } |
218 | |
219 | |
220 | void SQLiteStatementImpl::clear() |
221 | { |
222 | _columns[currentDataSet()].clear(); |
223 | _affectedRowCount = POCO_SQLITE_INV_ROW_CNT; |
224 | |
225 | if (_pStmt) |
226 | { |
227 | sqlite3_finalize(_pStmt); |
228 | _pStmt=0; |
229 | } |
230 | _pLeftover = 0; |
231 | } |
232 | |
233 | |
234 | bool SQLiteStatementImpl::hasNext() |
235 | { |
236 | if (_stepCalled) |
237 | return (_nextResponse == SQLITE_ROW); |
238 | |
239 | // _pStmt is allowed to be null for conditional SQL statements |
240 | if (_pStmt == 0) |
241 | { |
242 | _stepCalled = true; |
243 | _nextResponse = SQLITE_DONE; |
244 | return false; |
245 | } |
246 | |
247 | _stepCalled = true; |
248 | _nextResponse = sqlite3_step(_pStmt); |
249 | |
250 | if (_affectedRowCount == POCO_SQLITE_INV_ROW_CNT) _affectedRowCount = 0; |
251 | if (!sqlite3_stmt_readonly(_pStmt)) |
252 | _affectedRowCount += sqlite3_changes(_pDB); |
253 | |
254 | if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE) |
255 | Utility::throwException(_pDB, _nextResponse); |
256 | |
257 | _pExtractor->reset();//clear the cached null indicators |
258 | |
259 | return (_nextResponse == SQLITE_ROW); |
260 | } |
261 | |
262 | |
263 | std::size_t SQLiteStatementImpl::next() |
264 | { |
265 | if (SQLITE_ROW == _nextResponse) |
266 | { |
267 | poco_assert (columnsReturned() == sqlite3_column_count(_pStmt)); |
268 | |
269 | Extractions& = extractions(); |
270 | Extractions::iterator it = extracts.begin(); |
271 | Extractions::iterator itEnd = extracts.end(); |
272 | std::size_t pos = 0; // sqlite starts with pos 0 for results! |
273 | for (; it != itEnd; ++it) |
274 | { |
275 | (*it)->extract(pos); |
276 | pos += (*it)->numOfColumnsHandled(); |
277 | _isExtracted = true; |
278 | } |
279 | _stepCalled = false; |
280 | if (_affectedRowCount == POCO_SQLITE_INV_ROW_CNT) _affectedRowCount = 0; |
281 | |
282 | if (extracts.size()) |
283 | _affectedRowCount += static_cast<int>((*extracts.begin())->numOfRowsHandled()); |
284 | else |
285 | { |
286 | _affectedRowCount += static_cast<int>((*extracts.begin())->numOfRowsHandled()); |
287 | } |
288 | } |
289 | else if (SQLITE_DONE == _nextResponse) |
290 | { |
291 | throw Poco::SQL::SQLException("No data received" ); |
292 | } |
293 | else |
294 | { |
295 | Utility::throwException(_pDB, _nextResponse, std::string("Iterator Error: trying to access the next value" )); |
296 | } |
297 | |
298 | return 1u; |
299 | } |
300 | |
301 | |
302 | std::size_t SQLiteStatementImpl::columnsReturned() const |
303 | { |
304 | return (std::size_t) _columns[currentDataSet()].size(); |
305 | } |
306 | |
307 | |
308 | const MetaColumn& SQLiteStatementImpl::metaColumn(std::size_t pos, std::size_t dataSet) const |
309 | { |
310 | poco_assert (pos >= 0 && pos <= _columns[dataSet].size()); |
311 | return _columns[dataSet][pos]; |
312 | } |
313 | |
314 | |
315 | int SQLiteStatementImpl::affectedRowCount() const |
316 | { |
317 | if (_affectedRowCount != POCO_SQLITE_INV_ROW_CNT) return _affectedRowCount; |
318 | return _pStmt == 0 || sqlite3_stmt_readonly(_pStmt) ? 0 : sqlite3_changes(_pDB); |
319 | } |
320 | |
321 | |
322 | } } } // namespace Poco::SQL::SQLite |
323 | |