1 | // |
2 | // RecordSet.cpp |
3 | // |
4 | // Library: Data |
5 | // Package: DataCore |
6 | // Module: RecordSet |
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/RecordSet.h" |
16 | #include "Poco/Data/RowFilter.h" |
17 | #include "Poco/Data/Date.h" |
18 | #include "Poco/Data/Time.h" |
19 | #include "Poco/Data/DataException.h" |
20 | #include "Poco/DateTime.h" |
21 | #include "Poco/UTFString.h" |
22 | |
23 | |
24 | using namespace Poco::Data::Keywords; |
25 | using Poco::DateTime; |
26 | using Poco::UTF16String; |
27 | |
28 | |
29 | namespace Poco { |
30 | namespace Data { |
31 | |
32 | |
33 | RecordSet::RecordSet(const Statement& rStatement, |
34 | RowFormatter::Ptr pRowFormatter): |
35 | Statement(rStatement), |
36 | _currentRow(0), |
37 | _pBegin(new RowIterator(this, 0 == rowsExtracted())), |
38 | _pEnd(new RowIterator(this, true)) |
39 | { |
40 | if (pRowFormatter) setRowFormatter(pRowFormatter); |
41 | } |
42 | |
43 | |
44 | RecordSet::RecordSet(Session& rSession, |
45 | const std::string& query, |
46 | RowFormatter::Ptr pRowFormatter): |
47 | Statement((rSession << query, now)), |
48 | _currentRow(0), |
49 | _pBegin(new RowIterator(this, 0 == rowsExtracted())), |
50 | _pEnd(new RowIterator(this, true)) |
51 | { |
52 | if (pRowFormatter) setRowFormatter(pRowFormatter); |
53 | } |
54 | |
55 | |
56 | RecordSet::RecordSet(const RecordSet& other): |
57 | Statement(other.impl()), |
58 | _currentRow(other._currentRow), |
59 | _pBegin(new RowIterator(this, 0 == rowsExtracted())), |
60 | _pEnd(new RowIterator(this, true)), |
61 | _pFilter(other._pFilter) |
62 | { |
63 | } |
64 | |
65 | |
66 | RecordSet::~RecordSet() |
67 | { |
68 | try |
69 | { |
70 | delete _pBegin; |
71 | delete _pEnd; |
72 | |
73 | RowMap::iterator it = _rowMap.begin(); |
74 | RowMap::iterator itEnd = _rowMap.end(); |
75 | for (; it != itEnd; ++it) delete it->second; |
76 | } |
77 | catch (...) |
78 | { |
79 | poco_unexpected(); |
80 | } |
81 | } |
82 | |
83 | |
84 | RecordSet& RecordSet::reset(const Statement& stmt) |
85 | { |
86 | delete _pBegin; |
87 | _pBegin = 0; |
88 | delete _pEnd; |
89 | _pEnd = 0; |
90 | _currentRow = 0; |
91 | Statement::setTotalRowCount(StatementImpl::UNKNOWN_TOTAL_ROW_COUNT); |
92 | |
93 | RowMap::iterator it = _rowMap.begin(); |
94 | RowMap::iterator end = _rowMap.end(); |
95 | for (; it != end; ++it) delete it->second; |
96 | _rowMap.clear(); |
97 | |
98 | Statement::operator = (stmt); |
99 | |
100 | _pBegin = new RowIterator(this, 0 == rowsExtracted()); |
101 | _pEnd = new RowIterator(this, true); |
102 | |
103 | return *this; |
104 | } |
105 | |
106 | |
107 | Poco::Dynamic::Var RecordSet::value(std::size_t col, std::size_t dataRow, bool useFilter) const |
108 | { |
109 | if (useFilter && isFiltered() && !isAllowed(dataRow)) |
110 | throw InvalidAccessException("Row not allowed" ); |
111 | |
112 | if (isNull(col, dataRow)) return Poco::Dynamic::Var(); |
113 | |
114 | switch (columnType(col)) |
115 | { |
116 | case MetaColumn::FDT_BOOL: return value<bool>(col, dataRow, useFilter); |
117 | case MetaColumn::FDT_INT8: return value<Int8>(col, dataRow, useFilter); |
118 | case MetaColumn::FDT_UINT8: return value<UInt8>(col, dataRow, useFilter); |
119 | case MetaColumn::FDT_INT16: return value<Int16>(col, dataRow, useFilter); |
120 | case MetaColumn::FDT_UINT16: return value<UInt16>(col, dataRow, useFilter); |
121 | case MetaColumn::FDT_INT32: return value<Int32>(col, dataRow, useFilter); |
122 | case MetaColumn::FDT_UINT32: return value<UInt32>(col, dataRow, useFilter); |
123 | case MetaColumn::FDT_INT64: return value<Int64>(col, dataRow, useFilter); |
124 | case MetaColumn::FDT_UINT64: return value<UInt64>(col, dataRow, useFilter); |
125 | case MetaColumn::FDT_FLOAT: return value<float>(col, dataRow, useFilter); |
126 | case MetaColumn::FDT_DOUBLE: return value<double>(col, dataRow, useFilter); |
127 | case MetaColumn::FDT_STRING: return value<std::string>(col, dataRow, useFilter); |
128 | case MetaColumn::FDT_WSTRING: return value<UTF16String>(col, dataRow, useFilter); |
129 | case MetaColumn::FDT_BLOB: return value<BLOB>(col, dataRow, useFilter); |
130 | case MetaColumn::FDT_CLOB: return value<CLOB>(col, dataRow, useFilter); |
131 | case MetaColumn::FDT_DATE: return value<Date>(col, dataRow, useFilter); |
132 | case MetaColumn::FDT_TIME: return value<Time>(col, dataRow, useFilter); |
133 | case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, dataRow); |
134 | default: |
135 | throw UnknownTypeException("Data type not supported." ); |
136 | } |
137 | } |
138 | |
139 | |
140 | Poco::Dynamic::Var RecordSet::value(const std::string& name, std::size_t dataRow, bool useFilter) const |
141 | { |
142 | if (useFilter && isFiltered() && !isAllowed(dataRow)) |
143 | throw InvalidAccessException("Row not allowed" ); |
144 | |
145 | if (isNull(metaColumn(name).position(), dataRow)) return Poco::Dynamic::Var(); |
146 | |
147 | switch (columnType(name)) |
148 | { |
149 | case MetaColumn::FDT_BOOL: return value<bool>(name, dataRow, useFilter); |
150 | case MetaColumn::FDT_INT8: return value<Int8>(name, dataRow, useFilter); |
151 | case MetaColumn::FDT_UINT8: return value<UInt8>(name, dataRow, useFilter); |
152 | case MetaColumn::FDT_INT16: return value<Int16>(name, dataRow, useFilter); |
153 | case MetaColumn::FDT_UINT16: return value<UInt16>(name, dataRow, useFilter); |
154 | case MetaColumn::FDT_INT32: return value<Int32>(name, dataRow, useFilter); |
155 | case MetaColumn::FDT_UINT32: return value<UInt32>(name, dataRow, useFilter); |
156 | case MetaColumn::FDT_INT64: return value<Int64>(name, dataRow, useFilter); |
157 | case MetaColumn::FDT_UINT64: return value<UInt64>(name, dataRow, useFilter); |
158 | case MetaColumn::FDT_FLOAT: return value<float>(name, dataRow, useFilter); |
159 | case MetaColumn::FDT_DOUBLE: return value<double>(name, dataRow, useFilter); |
160 | case MetaColumn::FDT_STRING: return value<std::string>(name, dataRow, useFilter); |
161 | case MetaColumn::FDT_WSTRING: return value<UTF16String>(name, dataRow, useFilter); |
162 | case MetaColumn::FDT_BLOB: return value<BLOB>(name, dataRow, useFilter); |
163 | case MetaColumn::FDT_DATE: return value<Date>(name, dataRow, useFilter); |
164 | case MetaColumn::FDT_TIME: return value<Time>(name, dataRow, useFilter); |
165 | case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, dataRow, useFilter); |
166 | default: |
167 | throw UnknownTypeException("Data type not supported." ); |
168 | } |
169 | } |
170 | |
171 | |
172 | Row& RecordSet::row(std::size_t pos) |
173 | { |
174 | std::size_t rowCnt = rowCount(); |
175 | if (0 == rowCnt || pos > rowCnt - 1) |
176 | throw RangeException("Invalid recordset row requested." ); |
177 | |
178 | RowMap::const_iterator it = _rowMap.find(pos); |
179 | Row* pRow = 0; |
180 | std::size_t columns = columnCount(); |
181 | if (it == _rowMap.end()) |
182 | { |
183 | if (_rowMap.size()) |
184 | { |
185 | //reuse first row column names and sorting fields to save some memory |
186 | pRow = new Row(_rowMap.begin()->second->names(), |
187 | _rowMap.begin()->second->getSortMap(), |
188 | getRowFormatter()); |
189 | |
190 | for (std::size_t col = 0; col < columns; ++col) |
191 | pRow->set(col, value(col, pos)); |
192 | } |
193 | else |
194 | { |
195 | pRow = new Row; |
196 | pRow->setFormatter(getRowFormatter()); |
197 | for (std::size_t col = 0; col < columns; ++col) |
198 | pRow->append(metaColumn(static_cast<UInt32>(col)).name(), value(col, pos)); |
199 | } |
200 | |
201 | _rowMap.insert(RowMap::value_type(pos, pRow)); |
202 | } |
203 | else |
204 | { |
205 | pRow = it->second; |
206 | poco_check_ptr (pRow); |
207 | } |
208 | |
209 | return *pRow; |
210 | } |
211 | |
212 | |
213 | std::size_t RecordSet::rowCount() const |
214 | { |
215 | if (0 == extractions().size() && 0 == columnsExtracted()) |
216 | return 0; |
217 | poco_assert (extractions().size()); |
218 | std::size_t rc = storageRowCount(); |
219 | if (!isFiltered()) return rc; |
220 | |
221 | std::size_t counter = 0; |
222 | for (int dataRow = 0; dataRow < rc; ++dataRow) |
223 | { |
224 | if (isAllowed(dataRow)) ++counter; |
225 | } |
226 | |
227 | return counter; |
228 | } |
229 | |
230 | |
231 | bool RecordSet::isAllowed(std::size_t dataRow) const |
232 | { |
233 | if (!isFiltered()) return true; |
234 | return _pFilter->isAllowed(dataRow); |
235 | } |
236 | |
237 | |
238 | bool RecordSet::moveFirst() |
239 | { |
240 | const size_t rc = storageRowCount(); |
241 | if (rc > 0) |
242 | { |
243 | if (!isFiltered()) |
244 | { |
245 | _currentRow = 0; |
246 | return true; |
247 | } |
248 | |
249 | std::size_t currentRow = 0; |
250 | while (!isAllowed(currentRow)) |
251 | { |
252 | if (currentRow >= rc - 1) return false; |
253 | ++currentRow; |
254 | } |
255 | |
256 | _currentRow = currentRow; |
257 | return true; |
258 | } |
259 | else return false; |
260 | } |
261 | |
262 | |
263 | bool RecordSet::moveNext() |
264 | { |
265 | std::size_t currentRow = _currentRow; |
266 | do |
267 | { |
268 | if (currentRow >= storageRowCount() -1) return false; |
269 | ++currentRow; |
270 | } while (isFiltered() && !isAllowed(currentRow)); |
271 | |
272 | _currentRow = currentRow; |
273 | return true; |
274 | } |
275 | |
276 | |
277 | bool RecordSet::movePrevious() |
278 | { |
279 | std::size_t currentRow = _currentRow; |
280 | do |
281 | { |
282 | if (currentRow <= 0) return false; |
283 | --currentRow; |
284 | } while (isFiltered() && !isAllowed(currentRow)); |
285 | |
286 | _currentRow = currentRow; |
287 | return true; |
288 | } |
289 | |
290 | |
291 | bool RecordSet::moveLast() |
292 | { |
293 | if (storageRowCount() > 0) |
294 | { |
295 | std::size_t currentRow = subTotalRowCount() - 1; |
296 | if (!isFiltered()) |
297 | { |
298 | _currentRow = currentRow; |
299 | return true; |
300 | } |
301 | |
302 | while (!isAllowed(currentRow)) |
303 | { |
304 | if (currentRow <= 0) return false; |
305 | --currentRow; |
306 | } |
307 | |
308 | _currentRow = currentRow; |
309 | return true; |
310 | } |
311 | else return false; |
312 | } |
313 | |
314 | |
315 | void RecordSet::setRowFormatter(RowFormatter::Ptr pRowFormatter) |
316 | { |
317 | if (pRowFormatter) |
318 | { |
319 | if (pRowFormatter->getTotalRowCount() == RowFormatter::INVALID_ROW_COUNT) |
320 | pRowFormatter->setTotalRowCount(static_cast<int>(getTotalRowCount())); |
321 | |
322 | Statement::setRowFormatter(pRowFormatter); |
323 | RowMap::iterator it = _rowMap.begin(); |
324 | RowMap::iterator itEnd = _rowMap.end(); |
325 | for (; it != itEnd; ++it) it->second->setFormatter(getRowFormatter()); |
326 | } |
327 | else |
328 | throw NullPointerException("Null RowFormatter in RecordSet." ); |
329 | } |
330 | |
331 | |
332 | std::ostream& RecordSet::copyNames(std::ostream& os) const |
333 | { |
334 | if (begin() == end()) return os; |
335 | |
336 | std::string names = (*_pBegin)->namesToString(); |
337 | if (!names.empty()) os << names; |
338 | return os; |
339 | } |
340 | |
341 | |
342 | std::ostream& RecordSet::copyValues(std::ostream& os, std::size_t offset, std::size_t length) const |
343 | { |
344 | if (begin() == end()) return os; |
345 | |
346 | RowIterator it = *_pBegin + offset; |
347 | RowIterator itEnd = (RowIterator::POSITION_END != length) ? it + length : *_pEnd; |
348 | std::copy(it, itEnd, std::ostream_iterator<Row>(os)); |
349 | return os; |
350 | } |
351 | |
352 | |
353 | void RecordSet::formatValues(std::size_t offset, std::size_t length) const |
354 | { |
355 | if (begin() == end()) return; |
356 | |
357 | RowIterator it = *_pBegin + offset; |
358 | RowIterator itEnd = (RowIterator::POSITION_END != length) ? it + length : *_pEnd; |
359 | std::string val; |
360 | for (; it != itEnd; ++it) it->formatValues(); |
361 | } |
362 | |
363 | |
364 | std::ostream& RecordSet::copy(std::ostream& os, std::size_t offset, std::size_t length) const |
365 | { |
366 | if (begin() == end()) return os; |
367 | |
368 | RowFormatter& rf = const_cast<RowFormatter&>((*_pBegin)->getFormatter()); |
369 | rf.setTotalRowCount(static_cast<int>(getTotalRowCount())); |
370 | if (RowFormatter::FORMAT_PROGRESSIVE == rf.getMode()) |
371 | { |
372 | os << rf.prefix(); |
373 | copyNames(os); |
374 | copyValues(os, offset, length); |
375 | os << rf.postfix(); |
376 | } |
377 | else |
378 | { |
379 | formatNames(); |
380 | formatValues(offset, length); |
381 | os << rf.toString(); |
382 | } |
383 | |
384 | return os; |
385 | } |
386 | |
387 | |
388 | void RecordSet::filter(const Poco::AutoPtr<RowFilter>& pFilter) |
389 | { |
390 | _pFilter = pFilter; |
391 | } |
392 | |
393 | |
394 | bool RecordSet::isFiltered() const |
395 | { |
396 | return _pFilter && !_pFilter->isEmpty(); |
397 | } |
398 | |
399 | |
400 | } } // namespace Poco::Data |
401 | |