1//
2// ODBCStatementImpl.cpp
3//
4// Library: Data/ODBC
5// Package: ODBC
6// Module: ODBCStatementImpl
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/Data/ODBC/ODBCStatementImpl.h"
16#include "Poco/Data/ODBC/ConnectionHandle.h"
17#include "Poco/Data/ODBC/Utility.h"
18#include "Poco/Data/ODBC/ODBCException.h"
19#include "Poco/Data/AbstractPreparation.h"
20#include "Poco/Exception.h"
21
22
23#ifdef POCO_OS_FAMILY_WINDOWS
24 #pragma warning(disable:4312)// 'type cast' : conversion from 'std::size_t' to 'SQLPOINTER' of greater size
25#endif
26
27
28using Poco::DataFormatException;
29
30
31namespace Poco {
32namespace Data {
33namespace ODBC {
34
35
36const std::string ODBCStatementImpl::INVALID_CURSOR_STATE = "24000";
37
38
39ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
40 Poco::Data::StatementImpl(rSession),
41 _rConnection(rSession.dbc()),
42 _stmt(rSession.dbc()),
43 _stepCalled(false),
44 _nextResponse(0),
45 _prepared(false),
46 _affectedRowCount(0),
47 _canCompile(true),
48 _insertHint(false)
49{
50 int queryTimeout = rSession.queryTimeout();
51 if (queryTimeout >= 0)
52 {
53 SQLULEN uqt = static_cast<SQLULEN>(queryTimeout);
54 SQLSetStmtAttr(_stmt,
55 SQL_ATTR_QUERY_TIMEOUT,
56 (SQLPOINTER) uqt,
57 0);
58 }
59 SQLSMALLINT t;
60 SQLRETURN r = Poco::Data::ODBC::SQLGetInfo(_rConnection, SQL_DRIVER_NAME, NULL, 0, &t);
61 if (!Utility::isError(r) && t > 0)
62 {
63 std::string serverString;
64 serverString.resize(static_cast<std::size_t>(t) + 2);
65 r = Poco::Data::ODBC::SQLGetInfo(_rConnection, SQL_DRIVER_NAME, &serverString[0], SQLSMALLINT((serverString.length() - 1) * sizeof(serverString[0])), &t);
66 }
67}
68
69
70ODBCStatementImpl::~ODBCStatementImpl()
71{
72 ColumnPtrVecVec::iterator it = _columnPtrs.begin();
73 ColumnPtrVecVec::iterator end = _columnPtrs.end();
74 for (; it != end; ++it)
75 {
76 ColumnPtrVec::iterator itC = it->begin();
77 ColumnPtrVec::iterator endC = it->end();
78 for (; itC != endC; ++itC) delete *itC;
79 }
80}
81
82
83void ODBCStatementImpl::compileImpl()
84{
85 if (!_canCompile) return;
86
87 _stepCalled = false;
88 _nextResponse = 0;
89
90 if (_preparations.size())
91 PreparatorVec().swap(_preparations);
92
93 addPreparator();
94
95 Binder::ParameterBinding bind = session().getFeature("autoBind") ?
96 Binder::PB_IMMEDIATE : Binder::PB_AT_EXEC;
97
98 TypeInfo* pDT = 0;
99 try
100 {
101 Poco::Any dti = session().getProperty("dataTypeInfo");
102 pDT = AnyCast<TypeInfo*>(dti);
103 }
104 catch (NotSupportedException&)
105 {
106 }
107
108 const std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
109
110 _pBinder = new Binder(_stmt, maxFieldSize, bind, pDT, _insertHint);
111
112 makeInternalExtractors();
113 doPrepare();
114
115 _canCompile = false;
116}
117
118
119void ODBCStatementImpl::makeInternalExtractors()
120{
121 if (hasData() && !extractions().size())
122 {
123 try
124 {
125 fillColumns(currentDataSet());
126 }
127 catch (DataFormatException&)
128 {
129 if (isStoredProcedure()) return;
130 throw;
131 }
132
133 makeExtractors(columnsReturned());
134 fixupExtraction();
135 }
136}
137
138
139bool ODBCStatementImpl::addPreparator(bool addAlways)
140{
141 Preparator* prep = 0;
142 if (0 == _preparations.size())
143 {
144 std::string statement(toString());
145 if (statement.empty())
146 throw ODBCException("Empty statements are illegal");
147
148 Preparator::DataExtraction ext = session().getFeature("autoExtract") ?
149 Preparator::DE_BOUND : Preparator::DE_MANUAL;
150
151 std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
152
153 prep = new Preparator(_stmt, statement, maxFieldSize, ext);
154 }
155 else
156 prep = new Preparator(*_preparations[0]);
157 if (addAlways || prep->columns() > 0)
158 {
159 _preparations.push_back(prep);
160 _extractors.push_back(new Extractor(_stmt, _preparations.back()));
161
162 return true;
163 }
164
165 delete prep;
166 return false;
167}
168
169
170void ODBCStatementImpl::doPrepare()
171{
172 if (session().getFeature("autoExtract") && hasData())
173 {
174 std::size_t curDataSet = currentDataSet();
175 poco_check_ptr (_preparations[curDataSet]);
176
177 Extractions& extracts = extractions();
178 Extractions::iterator it = extracts.begin();
179 Extractions::iterator itEnd = extracts.end();
180
181 if (it != itEnd && (*it)->isBulk())
182 {
183 std::size_t limit = getExtractionLimit();
184 if (limit == Limit::LIMIT_UNLIMITED)
185 throw InvalidArgumentException("Bulk operation not allowed without limit.");
186 checkError(Poco::Data::ODBC::SQLSetStmtAttr(_stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) limit, 0),
187 "SQLSetStmtAttr(SQL_ATTR_ROW_ARRAY_SIZE)");
188 }
189
190 AbstractPreparation::Ptr pAP = 0;
191 Poco::Data::AbstractPreparator::Ptr pP = _preparations[curDataSet];
192 for (std::size_t pos = 0; it != itEnd; ++it)
193 {
194 pAP = (*it)->createPreparation(pP, pos);
195 pAP->prepare();
196 pos += (*it)->numOfColumnsHandled();
197 }
198
199 _prepared = true;
200 }
201}
202
203
204bool ODBCStatementImpl::canBind() const
205{
206 if (!bindings().empty())
207 return (*bindings().begin())->canBind();
208
209 return false;
210}
211
212
213void ODBCStatementImpl::doBind()
214{
215 this->clear();
216 Bindings& binds = bindings();
217 if (!binds.empty())
218 {
219 Bindings::iterator it = binds.begin();
220 Bindings::iterator itEnd = binds.end();
221
222 if (it != itEnd && 0 == _affectedRowCount)
223 _affectedRowCount = static_cast<std::size_t>((*it)->numOfRowsHandled());
224
225 for (std::size_t pos = 0; it != itEnd && (*it)->canBind(); ++it)
226 {
227 (*it)->bind(pos);
228 pos += (*it)->numOfColumnsHandled();
229 }
230 }
231}
232
233
234void ODBCStatementImpl::bindImpl()
235{
236 doBind();
237 SQLRETURN rc = SQLExecute(_stmt);
238
239 if (SQL_NEED_DATA == rc) putData();
240 else checkError(rc, "SQLExecute()");
241
242 _pBinder->synchronize();
243}
244
245
246void ODBCStatementImpl::putData()
247{
248 SQLPOINTER pParam = 0;
249 SQLINTEGER dataSize = 0;
250 SQLRETURN rc;
251
252 while (SQL_NEED_DATA == (rc = SQLParamData(_stmt, &pParam)))
253 {
254 if (pParam)
255 {
256 dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
257
258 if (Utility::isError(SQLPutData(_stmt, pParam, dataSize)))
259 throw StatementException(_stmt, "SQLPutData()");
260 }
261 else // if pParam is null pointer, do a dummy call
262 {
263 char dummy = 0;
264 if (Utility::isError(SQLPutData(_stmt, &dummy, 0)))
265 throw StatementException(_stmt, "SQLPutData()");
266 }
267 }
268
269 checkError(rc, "SQLParamData()");
270}
271
272
273void ODBCStatementImpl::clear()
274{
275 SQLRETURN rc = SQLCloseCursor(_stmt);
276 _stepCalled = false;
277 _affectedRowCount = 0;
278
279 if (Utility::isError(rc))
280 {
281 StatementError err(_stmt);
282 bool ignoreError = false;
283
284 const StatementDiagnostics& diagnostics = err.diagnostics();
285 //ignore "Invalid cursor state" error
286 //(returned by 3.x drivers when cursor is not opened)
287 for (int i = 0; i < diagnostics.count(); ++i)
288 {
289 if ((ignoreError =
290 (INVALID_CURSOR_STATE == std::string(diagnostics.sqlState(i)))))
291 {
292 break;
293 }
294 }
295
296 if (!ignoreError)
297 throw StatementException(_stmt, "SQLCloseCursor()");
298 }
299}
300
301bool ODBCStatementImpl::nextResultSet()
302{
303 SQLRETURN ret = SQLMoreResults(_stmt);
304
305 if (SQL_NO_DATA == ret)
306 return false;
307
308 if (Utility::isError(ret))
309 throw StatementException(_stmt, "SQLMoreResults()");
310
311 // need to remove old bindings, as Sybase doesn't like old ones
312 if (Utility::isError(SQLFreeStmt(_stmt, SQL_UNBIND)))
313 throw StatementException(_stmt, "SQLFreeStmt(SQL_UNBIND)");
314
315 return true;
316}
317
318
319bool ODBCStatementImpl::hasNext()
320{
321 if (hasData())
322 {
323 if (!extractions().size())
324 makeInternalExtractors();
325
326 if (!_prepared) doPrepare();
327
328 if (_stepCalled)
329 return _stepCalled = nextRowReady();
330
331 makeStep();
332
333 if (!nextRowReady())
334 {
335 // have a loop here, as there could be one or more empty results
336 do
337 {
338 if (hasMoreDataSets())
339 {
340 activateNextDataSet();
341 if (!nextResultSet())
342 return false;
343 addPreparator();
344 }
345 else
346 {
347 if (nextResultSet())
348 {
349 if (!addPreparator(false)) // skip the result set if it has no columns
350 continue;
351 fillColumns(currentDataSet() + 1);
352 makeExtractors(_preparations.back()->columns(), static_cast<Position::Type>(currentDataSet() + 1));
353 activateNextDataSet();
354 }
355 else return false;
356 }
357 doPrepare();
358 fixupExtraction();
359 makeStep();
360 } while (!nextRowReady());
361 }
362 else if (Utility::isError(_nextResponse))
363 checkError(_nextResponse, "SQLFetch()");
364
365 return true;
366 }
367
368 return false;
369}
370
371
372void ODBCStatementImpl::makeStep()
373{
374 _extractors[currentDataSet()]->reset();
375 _nextResponse = SQLFetch(_stmt);
376 checkError(_nextResponse);
377 _stepCalled = true;
378}
379
380
381std::size_t ODBCStatementImpl::next()
382{
383 std::size_t count = 0;
384
385 if (nextRowReady())
386 {
387 Extractions& extracts = extractions();
388 Extractions::iterator it = extracts.begin();
389 Extractions::iterator itEnd = extracts.end();
390 std::size_t prevCount = 0;
391 for (std::size_t pos = 0; it != itEnd; ++it)
392 {
393 count = (*it)->extract(pos);
394 if (prevCount && count != prevCount)
395 throw IllegalStateException("Different extraction counts");
396 prevCount = count;
397 pos += (*it)->numOfColumnsHandled();
398 }
399 _stepCalled = false;
400 }
401 else
402 {
403 throw StatementException(_stmt,
404 std::string("Next row not available."));
405 }
406
407 return count;
408}
409
410
411std::string ODBCStatementImpl::nativeSQL()
412{
413 std::string statement = toString();
414
415 SQLINTEGER length = (SQLINTEGER) statement.size() * 2;
416
417 char* pNative = 0;
418 SQLINTEGER retlen = length;
419 do
420 {
421 delete [] pNative;
422 pNative = new char[retlen];
423 std::memset(pNative, 0, retlen);
424 length = retlen;
425 if (Utility::isError(SQLNativeSql(_rConnection,
426 (SQLCHAR*) statement.c_str(),
427 (SQLINTEGER) statement.size(),
428 (SQLCHAR*) pNative,
429 length,
430 &retlen)))
431 {
432 delete [] pNative;
433 throw ConnectionException(_rConnection, "SQLNativeSql()");
434 }
435 ++retlen;//accommodate for terminating '\0'
436 }while (retlen > length);
437
438 std::string sql(pNative);
439 delete [] pNative;
440 return sql;
441}
442
443
444void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
445{
446 if (SQL_NO_DATA == rc) return;
447
448 if (Utility::isError(rc))
449 {
450 std::ostringstream os;
451 os << std::endl << "Requested SQL statement: " << toString() << std::endl;
452 os << "Native SQL statement: " << nativeSQL() << std::endl;
453 std::string str(msg); str += os.str();
454
455 throw StatementException(_stmt, str);
456 }
457}
458
459
460void ODBCStatementImpl::fillColumns(size_t dataSetPos)
461{
462 poco_assert_dbg(dataSetPos < _preparations.size());
463 poco_assert_dbg(_preparations[dataSetPos]);
464 std::size_t colCount = static_cast<std::size_t>(_preparations[dataSetPos]->columns());
465 if (dataSetPos >= _columnPtrs.size())
466 _columnPtrs.resize(dataSetPos + 1);
467
468 for (int i = 0; i < colCount; ++i)
469 _columnPtrs[dataSetPos].push_back(new ODBCMetaColumn(_stmt, i));
470}
471
472
473bool ODBCStatementImpl::isStoredProcedure() const
474{
475 std::string str = toString();
476 if (trimInPlace(str).size() < 2) return false;
477
478 return ('{' == str[0] && '}' == str[str.size()-1]);
479}
480
481
482const MetaColumn& ODBCStatementImpl::metaColumn(std::size_t pos, size_t dataSet) const
483{
484 poco_assert_dbg(dataSet < _columnPtrs.size());
485
486 std::size_t sz = _columnPtrs[dataSet].size();
487
488 if (0 == sz || pos > sz - 1)
489 throw InvalidAccessException(format("Invalid column number: %u", pos));
490
491 return *_columnPtrs[dataSet][pos];
492}
493
494
495int ODBCStatementImpl::affectedRowCount() const
496{
497 if (0 == _affectedRowCount)
498 {
499 SQLLEN rows = 0;
500 if (!Utility::isError(SQLRowCount(_stmt, &rows)))
501 _affectedRowCount = static_cast<std::size_t>(rows);
502 }
503
504 return static_cast<int>(_affectedRowCount);
505}
506
507
508void ODBCStatementImpl::insertHint()
509{
510 _insertHint = true;
511}
512
513
514} } } // namespace Poco::Data::ODBC
515