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 | |
20 | using Poco::InvalidArgumentException; |
21 | |
22 | |
23 | namespace Poco { |
24 | namespace Data { |
25 | namespace ODBC { |
26 | |
27 | |
28 | Preparator::Preparator(const StatementHandle& rStmt, |
29 | const std::string& statement, |
30 | std::size_t maxFieldSize, |
31 | 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 | |
51 | Preparator::Preparator(const Preparator& other): |
52 | _rStmt(other._rStmt), |
53 | _maxFieldSize(other._maxFieldSize), |
54 | _dataExtraction(other._dataExtraction) |
55 | { |
56 | resize(); |
57 | } |
58 | |
59 | |
60 | Preparator::~Preparator() |
61 | { |
62 | try |
63 | { |
64 | freeMemory(); |
65 | } |
66 | catch (...) |
67 | { |
68 | poco_unexpected(); |
69 | } |
70 | } |
71 | |
72 | |
73 | void 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 | |
132 | std::size_t Preparator::columns() const |
133 | { |
134 | if (_values.empty()) resize(); |
135 | return _values.size(); |
136 | } |
137 | |
138 | |
139 | void 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 | |
157 | std::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 | |
181 | std::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 | |
193 | void 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 | |