1//
2// Preparator.cpp
3//
4// Library: Data/ODBC
5// Package: ODBC
6// Module: Preparator
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/Preparator.h"
16#include "Poco/Data/ODBC/ODBCMetaColumn.h"
17#include "Poco/Exception.h"
18
19
20using Poco::InvalidArgumentException;
21
22
23namespace Poco {
24namespace Data {
25namespace ODBC {
26
27
28Preparator::Preparator(const StatementHandle& rStmt,
29 const std::string& statement,
30 std::size_t maxFieldSize,
31 DataExtraction dataExtraction) :
32 _rStmt(rStmt),
33 _maxFieldSize(maxFieldSize),
34 _dataExtraction(dataExtraction)
35{
36 SQLCHAR* pStr = (SQLCHAR*) statement.c_str();
37 if (Utility::isError(Poco::Data::ODBC::SQLPrepare(_rStmt, pStr, (SQLINTEGER) statement.length())))
38 throw StatementException(_rStmt);
39 // PostgreSQL error swallowing workaround:
40 // Postgres may execute a statement with syntax error fine,
41 // but will return error later
42 {
43 SQLSMALLINT t = 0;
44 SQLRETURN r = SQLNumResultCols(rStmt, &t);
45 if (r != SQL_NO_DATA && Utility::isError(r))
46 throw StatementException(rStmt, "Failed to get number of columns");
47 }
48}
49
50
51Preparator::Preparator(const Preparator& other):
52 _rStmt(other._rStmt),
53 _maxFieldSize(other._maxFieldSize),
54 _dataExtraction(other._dataExtraction)
55{
56 resize();
57}
58
59
60Preparator::~Preparator()
61{
62 try
63 {
64 freeMemory();
65 }
66 catch (...)
67 {
68 poco_unexpected();
69 }
70}
71
72
73void Preparator::freeMemory() const
74{
75 IndexMap::iterator it = _varLengthArrays.begin();
76 IndexMap::iterator end = _varLengthArrays.end();
77 for (; it != end; ++it)
78 {
79 switch (it->second)
80 {
81 case DT_BOOL:
82 deleteCachedArray<bool>(it->first);
83 break;
84
85 case DT_CHAR:
86 deleteCachedArray<char>(it->first);
87 break;
88
89 case DT_WCHAR:
90 deleteCachedArray<UTF16String::value_type>(it->first);
91 break;
92
93 case DT_UCHAR:
94 deleteCachedArray<unsigned char>(it->first);
95 break;
96
97 case DT_CHAR_ARRAY:
98 {
99 char** pc = AnyCast<char*>(&_values[it->first]);
100 if (pc) std::free(*pc);
101 break;
102 }
103
104 case DT_WCHAR_ARRAY:
105 {
106 UTF16String::value_type** pc = AnyCast<UTF16String::value_type*>(&_values[it->first]);
107 if (pc) std::free(*pc);
108 break;
109 }
110
111 case DT_UCHAR_ARRAY:
112 {
113 unsigned char** pc = AnyCast<unsigned char*>(&_values[it->first]);
114 if (pc) std::free(*pc);
115 break;
116 }
117
118 case DT_BOOL_ARRAY:
119 {
120 bool** pb = AnyCast<bool*>(&_values[it->first]);
121 if (pb) std::free(*pb);
122 break;
123 }
124
125 default:
126 throw InvalidArgumentException("Unknown data type.");
127 }
128 }
129}
130
131
132std::size_t Preparator::columns() const
133{
134 if (_values.empty()) resize();
135 return _values.size();
136}
137
138
139void Preparator::resize() const
140{
141 SQLSMALLINT nCol = 0;
142 int rc = SQLNumResultCols(_rStmt, &nCol);
143 if (!Utility::isError(rc) && (0 != nCol))
144 {
145 _values.resize(nCol, 0);
146 _lengths.resize(nCol, 0);
147 _lenLengths.resize(nCol);
148 if(_varLengthArrays.size())
149 {
150 freeMemory();
151 _varLengthArrays.clear();
152 }
153 }
154}
155
156
157std::size_t Preparator::maxDataSize(std::size_t pos) const
158{
159 poco_assert_dbg (pos < _values.size());
160
161 std::size_t sz = 0;
162 std::size_t maxsz = getMaxFieldSize();
163
164 try
165 {
166 ODBCMetaColumn mc(_rStmt, pos);
167 sz = mc.length();
168
169 // accommodate for terminating zero (non-bulk only!)
170 MetaColumn::ColumnDataType type = mc.type();
171 if (!isBulk() && ((ODBCMetaColumn::FDT_WSTRING == type) || (ODBCMetaColumn::FDT_STRING == type))) ++sz;
172 }
173 catch (StatementException&) { }
174
175 if (!sz || sz > maxsz) sz = maxsz;
176
177 return sz;
178}
179
180
181std::size_t Preparator::actualDataSize(std::size_t col, std::size_t row) const
182{
183 SQLLEN size = (POCO_DATA_INVALID_ROW == row) ? _lengths.at(col) :
184 _lenLengths.at(col).at(row);
185
186 // workaround for drivers returning negative length
187 if (size < 0 && SQL_NULL_DATA != size) size *= -1;
188
189 return size;
190}
191
192
193void Preparator::prepareBoolArray(std::size_t pos, SQLSMALLINT valueType, std::size_t length)
194{
195 poco_assert_dbg (DE_BOUND == _dataExtraction);
196 poco_assert_dbg (pos < _values.size());
197 poco_assert_dbg (pos < _lengths.size());
198 poco_assert_dbg (pos < _lenLengths.size());
199
200 bool* pArray = (bool*) std::calloc(length, sizeof(bool));
201
202 _values[pos] = Any(pArray);
203 _lengths[pos] = 0;
204 _lenLengths[pos].resize(length);
205 _varLengthArrays.insert(IndexMap::value_type(pos, DT_BOOL_ARRAY));
206
207 if (Utility::isError(SQLBindCol(_rStmt,
208 (SQLUSMALLINT) pos + 1,
209 valueType,
210 (SQLPOINTER) pArray,
211 (SQLINTEGER) sizeof(bool),
212 &_lenLengths[pos][0])))
213 {
214 throw StatementException(_rStmt, "SQLBindCol()");
215 }
216}
217
218
219} } } // namespace Poco::Data::ODBC
220