| 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 | |