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