1 //
2 // Binder.cpp
3 //
4 // Library: Data/ODBC
5 // Package: ODBC
6 // Module: Binder
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/Binder.h"
16 #include "Poco/Data/ODBC/Utility.h"
17 #include "Poco/Data/ODBC/Connector.h"
18 #include "Poco/Data/LOB.h"
19 #include "Poco/Data/ODBC/ODBCException.h"
20 #include "Poco/DateTime.h"
21 #include "Poco/Exception.h"
22 #include <sql.h>
23
24
25 namespace Poco {
26 namespace Data {
27 namespace ODBC {
28
29
Binder(const StatementHandle & rStmt,std::size_t maxFieldSize,Binder::ParameterBinding dataBinding,const TypeInfo * pDataTypes)30 Binder::Binder(const StatementHandle& rStmt,
31 std::size_t maxFieldSize,
32 Binder::ParameterBinding dataBinding,
33 const TypeInfo* pDataTypes):
34 _rStmt(rStmt),
35 _paramBinding(dataBinding),
36 _pTypeInfo(pDataTypes),
37 _paramSetSize(0),
38 _maxFieldSize(maxFieldSize)
39 {
40 }
41
42
~Binder()43 Binder::~Binder()
44 {
45 freeMemory();
46 }
47
48
freeMemory()49 void Binder::freeMemory()
50 {
51 LengthPtrVec::iterator itLen = _lengthIndicator.begin();
52 LengthPtrVec::iterator itLenEnd = _lengthIndicator.end();
53 for(; itLen != itLenEnd; ++itLen) delete *itLen;
54
55 LengthVecVec::iterator itVecLen = _vecLengthIndicator.begin();
56 LengthVecVec::iterator itVecLenEnd = _vecLengthIndicator.end();
57 for (; itVecLen != itVecLenEnd; ++itVecLen) delete *itVecLen;
58
59 TimeMap::iterator itT = _times.begin();
60 TimeMap::iterator itTEnd = _times.end();
61 for(; itT != itTEnd; ++itT) delete itT->first;
62
63 DateMap::iterator itD = _dates.begin();
64 DateMap::iterator itDEnd = _dates.end();
65 for(; itD != itDEnd; ++itD) delete itD->first;
66
67 TimestampMap::iterator itTS = _timestamps.begin();
68 TimestampMap::iterator itTSEnd = _timestamps.end();
69 for(; itTS != itTSEnd; ++itTS) delete itTS->first;
70
71 StringMap::iterator itStr = _strings.begin();
72 StringMap::iterator itStrEnd = _strings.end();
73 for(; itStr != itStrEnd; ++itStr) std::free(itStr->first);
74
75 CharPtrVec::iterator itChr = _charPtrs.begin();
76 CharPtrVec::iterator endChr = _charPtrs.end();
77 for (; itChr != endChr; ++itChr) std::free(*itChr);
78
79 UTF16CharPtrVec::iterator itUTF16Chr = _utf16CharPtrs.begin();
80 UTF16CharPtrVec::iterator endUTF16Chr = _utf16CharPtrs.end();
81 for (; itUTF16Chr != endUTF16Chr; ++itUTF16Chr) std::free(*itUTF16Chr);
82
83 BoolPtrVec::iterator itBool = _boolPtrs.begin();
84 BoolPtrVec::iterator endBool = _boolPtrs.end();
85 for (; itBool != endBool; ++itBool) delete [] *itBool;
86
87 DateVecVec::iterator itDateVec = _dateVecVec.begin();
88 DateVecVec::iterator itDateVecEnd = _dateVecVec.end();
89 for (; itDateVec != itDateVecEnd; ++itDateVec) delete *itDateVec;
90
91 TimeVecVec::iterator itTimeVec = _timeVecVec.begin();
92 TimeVecVec::iterator itTimeVecEnd = _timeVecVec.end();
93 for (; itTimeVec != itTimeVecEnd; ++itTimeVec) delete *itTimeVec;
94
95 DateTimeVecVec::iterator itDateTimeVec = _dateTimeVecVec.begin();
96 DateTimeVecVec::iterator itDateTimeVecEnd = _dateTimeVecVec.end();
97 for (; itDateTimeVec != itDateTimeVecEnd; ++itDateTimeVec) delete *itDateTimeVec;
98 }
99
100
bind(std::size_t pos,const std::string & val,Direction dir)101 void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
102 {
103 SQLPOINTER pVal = 0;
104 SQLINTEGER size = (SQLINTEGER) val.size();
105 SQLINTEGER colSize = 0;
106 SQLSMALLINT decDigits = 0;
107 getColSizeAndPrecision(pos, SQL_C_CHAR, colSize, decDigits, val.size());
108
109 if (isOutBound(dir))
110 {
111 getColumnOrParameterSize(pos, size);
112 char* pChar = (char*) std::calloc(size, sizeof(char));
113 pVal = (SQLPOINTER) pChar;
114 _outParams.insert(ParamMap::value_type(pVal, size));
115 _strings.insert(StringMap::value_type(pChar, const_cast<std::string*>(&val)));
116 }
117 else if (isInBound(dir))
118 {
119 pVal = (SQLPOINTER) val.c_str();
120 _inParams.insert(ParamMap::value_type(pVal, size));
121 }
122 else
123 throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
124
125 SQLLEN* pLenIn = new SQLLEN(SQL_NTS);
126
127 if (PB_AT_EXEC == _paramBinding)
128 *pLenIn = SQL_LEN_DATA_AT_EXEC(size);
129
130 _lengthIndicator.push_back(pLenIn);
131
132 if (Utility::isError(SQLBindParameter(_rStmt,
133 (SQLUSMALLINT) pos + 1,
134 toODBCDirection(dir),
135 SQL_C_CHAR,
136 Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
137 (SQLUINTEGER) colSize,
138 0,
139 pVal,
140 (SQLINTEGER) size,
141 _lengthIndicator.back())))
142 {
143 throw StatementException(_rStmt, "SQLBindParameter(std::string)");
144 }
145 }
146
147
bind(std::size_t pos,const UTF16String & val,Direction dir)148 void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir)
149 {
150 typedef UTF16String::value_type CharT;
151
152 SQLPOINTER pVal = 0;
153 SQLINTEGER size = (SQLINTEGER)(val.size() * sizeof(CharT));
154 SQLINTEGER colSize = 0;
155 SQLSMALLINT decDigits = 0;
156 getColSizeAndPrecision(pos, SQL_C_WCHAR, colSize, decDigits);
157
158 if (isOutBound(dir))
159 {
160 getColumnOrParameterSize(pos, size);
161 CharT* pChar = (CharT*)std::calloc(size, 1);
162 pVal = (SQLPOINTER)pChar;
163 _outParams.insert(ParamMap::value_type(pVal, size));
164 _utf16Strings.insert(UTF16StringMap::value_type(pChar, const_cast<UTF16String*>(&val)));
165 }
166 else if (isInBound(dir))
167 {
168 pVal = (SQLPOINTER)val.c_str();
169 _inParams.insert(ParamMap::value_type(pVal, size));
170 }
171 else
172 throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
173
174 SQLLEN* pLenIn = new SQLLEN(SQL_NTS);
175
176 if (PB_AT_EXEC == _paramBinding)
177 {
178 *pLenIn = SQL_LEN_DATA_AT_EXEC(size);
179 }
180
181 _lengthIndicator.push_back(pLenIn);
182
183 if (Utility::isError(SQLBindParameter(_rStmt,
184 (SQLUSMALLINT)pos + 1,
185 toODBCDirection(dir),
186 SQL_C_WCHAR,
187 SQL_WLONGVARCHAR,
188 (SQLUINTEGER)colSize,
189 0,
190 pVal,
191 (SQLINTEGER)size,
192 _lengthIndicator.back())))
193 {
194 throw StatementException(_rStmt, "SQLBindParameter(std::string)");
195 }
196 }
197
198
bind(std::size_t pos,const Date & val,Direction dir)199 void Binder::bind(std::size_t pos, const Date& val, Direction dir)
200 {
201 SQLINTEGER size = (SQLINTEGER) sizeof(SQL_DATE_STRUCT);
202 SQLLEN* pLenIn = new SQLLEN;
203 *pLenIn = size;
204
205 _lengthIndicator.push_back(pLenIn);
206
207 SQL_DATE_STRUCT* pDS = new SQL_DATE_STRUCT;
208 Utility::dateSync(*pDS, val);
209
210 _dates.insert(DateMap::value_type(pDS, const_cast<Date*>(&val)));
211
212 SQLINTEGER colSize = 0;
213 SQLSMALLINT decDigits = 0;
214 getColSizeAndPrecision(pos, SQL_TYPE_DATE, colSize, decDigits);
215
216 if (Utility::isError(SQLBindParameter(_rStmt,
217 (SQLUSMALLINT) pos + 1,
218 toODBCDirection(dir),
219 SQL_C_TYPE_DATE,
220 SQL_TYPE_DATE,
221 colSize,
222 decDigits,
223 (SQLPOINTER) pDS,
224 0,
225 _lengthIndicator.back())))
226 {
227 throw StatementException(_rStmt, "SQLBindParameter(Date)");
228 }
229 }
230
231
bind(std::size_t pos,const Time & val,Direction dir)232 void Binder::bind(std::size_t pos, const Time& val, Direction dir)
233 {
234 SQLINTEGER size = (SQLINTEGER) sizeof(SQL_TIME_STRUCT);
235 SQLLEN* pLenIn = new SQLLEN;
236 *pLenIn = size;
237
238 _lengthIndicator.push_back(pLenIn);
239
240 SQL_TIME_STRUCT* pTS = new SQL_TIME_STRUCT;
241 Utility::timeSync(*pTS, val);
242
243 _times.insert(TimeMap::value_type(pTS, const_cast<Time*>(&val)));
244
245 SQLINTEGER colSize = 0;
246 SQLSMALLINT decDigits = 0;
247 getColSizeAndPrecision(pos, SQL_TYPE_TIME, colSize, decDigits);
248
249 if (Utility::isError(SQLBindParameter(_rStmt,
250 (SQLUSMALLINT) pos + 1,
251 toODBCDirection(dir),
252 SQL_C_TYPE_TIME,
253 SQL_TYPE_TIME,
254 colSize,
255 decDigits,
256 (SQLPOINTER) pTS,
257 0,
258 _lengthIndicator.back())))
259 {
260 throw StatementException(_rStmt, "SQLBindParameter(Time)");
261 }
262 }
263
264
bind(std::size_t pos,const Poco::DateTime & val,Direction dir)265 void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
266 {
267 SQLINTEGER size = (SQLINTEGER) sizeof(SQL_TIMESTAMP_STRUCT);
268 SQLLEN* pLenIn = new SQLLEN;
269 *pLenIn = size;
270
271 _lengthIndicator.push_back(pLenIn);
272
273 SQL_TIMESTAMP_STRUCT* pTS = new SQL_TIMESTAMP_STRUCT;
274 Utility::dateTimeSync(*pTS, val);
275
276 _timestamps.insert(TimestampMap::value_type(pTS, const_cast<DateTime*>(&val)));
277
278 SQLINTEGER colSize = 0;
279 SQLSMALLINT decDigits = 0;
280 getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits);
281
282 if (Utility::isError(SQLBindParameter(_rStmt,
283 (SQLUSMALLINT) pos + 1,
284 toODBCDirection(dir),
285 SQL_C_TYPE_TIMESTAMP,
286 SQL_TYPE_TIMESTAMP,
287 colSize,
288 decDigits,
289 (SQLPOINTER) pTS,
290 0,
291 _lengthIndicator.back())))
292 {
293 throw StatementException(_rStmt, "SQLBindParameter(DateTime)");
294 }
295 }
296
297
bind(std::size_t pos,const NullData & val,Direction dir)298 void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
299 {
300 if (isOutBound(dir) || !isInBound(dir))
301 throw NotImplementedException("NULL parameter type can only be inbound.");
302
303 _inParams.insert(ParamMap::value_type(SQLPOINTER(0), SQLINTEGER(0)));
304
305 SQLLEN* pLenIn = new SQLLEN;
306 *pLenIn = SQL_NULL_DATA;
307
308 _lengthIndicator.push_back(pLenIn);
309
310 SQLINTEGER colSize = 0;
311 SQLSMALLINT decDigits = 0;
312 getColSizeAndPrecision(pos, SQL_C_STINYINT, colSize, decDigits);
313
314 if (Utility::isError(SQLBindParameter(_rStmt,
315 (SQLUSMALLINT) pos + 1,
316 SQL_PARAM_INPUT,
317 SQL_C_STINYINT,
318 Utility::sqlDataType(SQL_C_STINYINT),
319 colSize,
320 decDigits,
321 0,
322 0,
323 _lengthIndicator.back())))
324 {
325 throw StatementException(_rStmt, "SQLBindParameter()");
326 }
327 }
328
329
parameterSize(SQLPOINTER pAddr) const330 std::size_t Binder::parameterSize(SQLPOINTER pAddr) const
331 {
332 ParamMap::const_iterator it = _inParams.find(pAddr);
333 if (it != _inParams.end()) return it->second;
334
335 it = _outParams.find(pAddr);
336 if (it != _outParams.end()) return it->second;
337
338 throw NotFoundException("Requested data size not found.");
339 }
340
341
bind(std::size_t pos,const char * const & pVal,Direction dir)342 void Binder::bind(std::size_t pos, const char* const &pVal, Direction dir)
343 {
344 throw NotImplementedException("char* binding not implemented, Use std::string instead.");
345 }
346
347
toODBCDirection(Direction dir) const348 SQLSMALLINT Binder::toODBCDirection(Direction dir) const
349 {
350 bool in = isInBound(dir);
351 bool out = isOutBound(dir);
352 SQLSMALLINT ioType = SQL_PARAM_TYPE_UNKNOWN;
353 if (in && out) ioType = SQL_PARAM_INPUT_OUTPUT;
354 else if(in) ioType = SQL_PARAM_INPUT;
355 else if(out) ioType = SQL_PARAM_OUTPUT;
356 else throw Poco::IllegalStateException("Binder not bound (must be [in] OR [out]).");
357
358 return ioType;
359 }
360
361
synchronize()362 void Binder::synchronize()
363 {
364 if (_dates.size())
365 {
366 DateMap::iterator it = _dates.begin();
367 DateMap::iterator end = _dates.end();
368 for(; it != end; ++it)
369 Utility::dateSync(*it->second, *it->first);
370 }
371
372 if (_times.size())
373 {
374 TimeMap::iterator it = _times.begin();
375 TimeMap::iterator end = _times.end();
376 for(; it != end; ++it)
377 Utility::timeSync(*it->second, *it->first);
378 }
379
380 if (_timestamps.size())
381 {
382 TimestampMap::iterator it = _timestamps.begin();
383 TimestampMap::iterator end = _timestamps.end();
384 for(; it != end; ++it)
385 Utility::dateTimeSync(*it->second, *it->first);
386 }
387
388 if (_strings.size())
389 {
390 StringMap::iterator it = _strings.begin();
391 StringMap::iterator end = _strings.end();
392 for(; it != end; ++it)
393 it->second->assign(it->first, std::strlen(it->first));
394 }
395 }
396
397
reset()398 void Binder::reset()
399 {
400 freeMemory();
401 LengthPtrVec().swap(_lengthIndicator);
402 _inParams.clear();
403 _outParams.clear();
404 _dates.clear();
405 _times.clear();
406 _timestamps.clear();
407 _strings.clear();
408 _dateVecVec.clear();
409 _timeVecVec.clear();
410 _dateTimeVecVec.clear();
411 _charPtrs.clear();
412 _boolPtrs.clear();
413 _containers.clear();
414 _paramSetSize = 0;
415 }
416
417
getColSizeAndPrecision(std::size_t pos,SQLSMALLINT cDataType,SQLINTEGER & colSize,SQLSMALLINT & decDigits,std::size_t actualSize)418 void Binder::getColSizeAndPrecision(std::size_t pos,
419 SQLSMALLINT cDataType,
420 SQLINTEGER& colSize,
421 SQLSMALLINT& decDigits,
422 std::size_t actualSize)
423 {
424 // Not all drivers are equally willing to cooperate in this matter.
425 // Hence the funky flow control.
426 DynamicAny tmp;
427 bool found(false);
428 if (_pTypeInfo)
429 {
430 found = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp);
431 if (found) colSize = tmp;
432 if (actualSize > colSize)
433 {
434 throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)",
435 pos, actualSize, static_cast<long>(colSize)));
436 }
437 found = _pTypeInfo->tryGetInfo(cDataType, "MINIMUM_SCALE", tmp);
438 if (found)
439 {
440 decDigits = tmp;
441 return;
442 }
443 }
444
445 try
446 {
447 Parameter p(_rStmt, pos);
448 colSize = (SQLINTEGER) p.columnSize();
449 decDigits = (SQLSMALLINT) p.decimalDigits();
450 return;
451 }
452 catch (StatementException&)
453 {
454 }
455
456 try
457 {
458 ODBCMetaColumn c(_rStmt, pos);
459 colSize = (SQLINTEGER) c.length();
460 decDigits = (SQLSMALLINT) c.precision();
461 return;
462 }
463 catch (StatementException&)
464 {
465 }
466
467 // last check, just in case
468 if ((0 != colSize) && (actualSize > colSize))
469 {
470 throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)",
471 pos, actualSize, static_cast<long>(colSize)));
472 }
473
474 // no success, set to zero and hope for the best
475 // (most drivers do not require these most of the times anyway)
476 colSize = 0;
477 decDigits = 0;
478 return;
479 }
480
481
getColumnOrParameterSize(std::size_t pos,SQLINTEGER & size)482 void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
483 {
484 std::size_t colSize = 0;
485 std::size_t paramSize = 0;
486
487 try
488 {
489 ODBCMetaColumn col(_rStmt, pos);
490 colSize = col.length();
491 }
492 catch (StatementException&) { }
493
494 try
495 {
496 Parameter p(_rStmt, pos);
497 paramSize = p.columnSize();
498 }
499 catch (StatementException&)
500 {
501 size = DEFAULT_PARAM_SIZE;
502 //On Linux, PostgreSQL driver segfaults on SQLGetDescField, so this is disabled for now
503 #ifdef POCO_OS_FAMILY_WINDOWS
504 SQLHDESC hIPD = 0;
505 if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, SQL_IS_POINTER, 0)))
506 {
507 SQLUINTEGER sz = 0;
508 if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, SQL_IS_UINTEGER, 0)) &&
509 sz > 0)
510 {
511 size = sz;
512 }
513 }
514 #endif
515 }
516
517 if (colSize > 0 && paramSize > 0)
518 size = colSize < paramSize ? static_cast<SQLINTEGER>(colSize) : static_cast<SQLINTEGER>(paramSize);
519 else if (colSize > 0)
520 size = static_cast<SQLINTEGER>(colSize);
521 else if (paramSize > 0)
522 size = static_cast<SQLINTEGER>(paramSize);
523
524 if (size > _maxFieldSize) size = static_cast<SQLINTEGER>(_maxFieldSize);
525 }
526
527
setParamSetSize(std::size_t length)528 void Binder::setParamSetSize(std::size_t length)
529 {
530 if (0 == _paramSetSize)
531 {
532 if (Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, SQL_IS_UINTEGER)) ||
533 Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) length, SQL_IS_UINTEGER)))
534 throw StatementException(_rStmt, "SQLSetStmtAttr()");
535
536 _paramSetSize = static_cast<SQLINTEGER>(length);
537 }
538 }
539
540
541 } } } // namespace Poco::Data::ODBC
542