LibreOffice Module dbaccess (master) 1
rowinputbinary.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
24#include "rowinputbinary.hxx"
25#include <com/sun/star/sdbc/DataType.hpp>
26#include <com/sun/star/io/WrongFormatException.hpp>
27#include <com/sun/star/util/Time.hpp>
28#include <com/sun/star/util/Date.hpp>
29#include <com/sun/star/util/DateTime.hpp>
30
32#include <tools/stream.hxx>
33#include <rtl/ustrbuf.hxx>
34
35#include <boost/date_time/posix_time/posix_time.hpp>
36
37namespace
38{
45OUString lcl_double_dabble(const std::vector<sal_uInt8>& bytes)
46{
47 size_t nbits = 8 * bytes.size(); // length of array in bits
48 size_t nscratch = nbits / 2; // length of scratch in bytes
49 std::vector<char> scratch(nscratch, 0);
50
51 for (size_t i = 0; i < bytes.size(); ++i)
52 {
53 for (size_t j = 0; j < 8; ++j)
54 {
55 /* This bit will be shifted in on the right. */
56 int shifted_in = (bytes[i] & (1 << (7 - j))) ? 1 : 0;
57
58 /* Add 3 everywhere that scratch[k] >= 5. */
59 for (size_t k = 0; k < nscratch; ++k)
60 scratch[k] += (scratch[k] >= 5) ? 3 : 0;
61
62 /* Shift scratch to the left by one position. */
63 for (size_t k = 0; k < nscratch - 1; ++k)
64 {
65 scratch[k] <<= 1;
66 scratch[k] &= 0xF;
67 scratch[k] |= (scratch[k + 1] >= 8) ? 1 : 0;
68 }
69
70 /* Shift in the new bit from arr. */
71 scratch[nscratch - 1] <<= 1;
72 scratch[nscratch - 1] &= 0xF;
73 scratch[nscratch - 1] |= shifted_in;
74 }
75 }
76
77 auto it = scratch.begin();
78 /* Remove leading zeros from the scratch space. */
79 while (*it == 0 && scratch.size() > 1)
80 {
81 it = scratch.erase(it);
82 }
83
84 /* Convert the scratch space from BCD digits to ASCII. */
85 for (auto& digit : scratch)
86 digit += '0';
87
88 /* Resize and return the resulting string. */
89 return OStringToOUString(std::string_view(scratch.data(), scratch.size()),
90 RTL_TEXTENCODING_UTF8);
91}
92
93OUString lcl_makeStringFromBigint(std::vector<sal_uInt8>&& aBytes)
94{
95 OUStringBuffer sRet;
96
97 // two's complement
98 if ((aBytes[0] & 0x80) != 0)
99 {
100 sRet.append("-");
101 for (auto& byte : aBytes)
102 byte = ~byte;
103 // add 1 to byte array
104 // FIXME e.g. 10000 valid ?
105 for (size_t i = aBytes.size() - 1; i != 0; --i)
106 {
107 aBytes[i] += 1;
108 if (aBytes[i] != 0)
109 break;
110 }
111 }
112 // convert binary to BCD
113 OUString sNum = lcl_double_dabble(aBytes);
114 sRet.append(sNum);
115 return sRet.makeStringAndClear();
116}
117
118OUString lcl_putDot(std::u16string_view sNum, sal_Int32 nScale)
119{
120 // e.g. sNum = "0", nScale = 2 -> "0.00"
121 OUStringBuffer sBuf{ sNum };
122 sal_Int32 nNullsToAppend = nScale - sNum.size() + 1;
123 for (sal_Int32 i = 0; i < nNullsToAppend; ++i)
124 sBuf.insert(0, "0");
125
126 if (nScale > 0)
127 sBuf.insert(sBuf.getLength() - 1 - nScale, ".");
128 return sBuf.makeStringAndClear();
129}
130}
131
132namespace dbahsql
133{
134using namespace css::uno;
135using namespace css::sdbc;
136using namespace css::io;
137using namespace boost::posix_time;
138using namespace boost::gregorian;
139
141
142void HsqlRowInputStream::setInputStream(Reference<XInputStream> const& rStream)
143{
145 m_pStream->SetEndian(SvStreamEndian::BIG);
146}
147
149
150void HsqlRowInputStream::seek(sal_Int32 nPos) { m_pStream->Seek(nPos); }
151
153{
154 sal_Int32 nLen = 0;
155 m_pStream->ReadInt32(nLen);
156 return readUTF(nLen);
157}
158
159OUString HsqlRowInputStream::readUTF(sal_Int32 nUTFLen)
160{
161 Sequence<sal_Unicode> aBuffer(nUTFLen);
162 sal_Unicode* pStr = aBuffer.getArray();
163
164 sal_Int32 nCount = 0;
165 sal_Int32 nStrLen = 0;
166 while (nCount < nUTFLen)
167 {
168 sal_uInt8 c = 0;
169 m_pStream->ReadUChar(c);
170 sal_uInt8 char2, char3;
171 switch (c >> 4)
172 {
173 case 0:
174 case 1:
175 case 2:
176 case 3:
177 case 4:
178 case 5:
179 case 6:
180 case 7:
181 // 0xxxxxxx
182 nCount++;
183 pStr[nStrLen++] = c;
184 break;
185
186 case 12:
187 case 13:
188 // 110x xxxx 10xx xxxx
189 nCount += 2;
190 if (nCount > nUTFLen)
191 {
192 throw WrongFormatException();
193 }
194
195 m_pStream->ReadUChar(char2);
196 if ((char2 & 0xC0) != 0x80)
197 {
198 throw WrongFormatException();
199 }
200
201 pStr[nStrLen++] = (sal_Unicode(c & 0x1F) << 6) | (char2 & 0x3F);
202 break;
203
204 case 14:
205 // 1110 xxxx 10xx xxxx 10xx xxxx
206 nCount += 3;
207 if (nCount > nUTFLen)
208 {
209 throw WrongFormatException();
210 }
211
212 m_pStream->ReadUChar(char2);
213 m_pStream->ReadUChar(char3);
214
215 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
216 {
217 throw WrongFormatException();
218 }
219 pStr[nStrLen++] = (sal_Unicode(c & 0x0F) << 12) | (sal_Unicode(char2 & 0x3F) << 6)
220 | (char3 & 0x3F);
221 break;
222
223 default:
224 // 10xx xxxx, 1111 xxxx
225 throw WrongFormatException();
226 }
227 }
228 return OUString(pStr, nStrLen);
229}
230
232{
233 sal_uInt8 nNull = 0;
234 m_pStream->ReadUChar(nNull);
235 return nNull == 0;
236}
237
238std::vector<Any> HsqlRowInputStream::readOneRow(const std::vector<ColumnDefinition>& nColTypes)
239{
240 auto nLen = nColTypes.size();
241 std::vector<Any> aData;
242
243 for (size_t i = 0; i < nLen; ++i)
244 {
245 if (checkNull())
246 {
247 aData.push_back(Any());
248 continue;
249 }
250
251 sal_Int32 nType = nColTypes[i].getDataType();
252
253 // TODO throw error on EoF
254
255 switch (nType)
256 {
257 case DataType::CHAR:
258 case DataType::VARCHAR:
259 case DataType::LONGVARCHAR:
260 aData.push_back(Any(readString()));
261 break;
262 case DataType::TINYINT:
263 case DataType::SMALLINT:
264 {
265 sal_Int16 value = 0;
266 m_pStream->ReadInt16(value);
267 aData.push_back(Any(value));
268 }
269 break;
270 case DataType::INTEGER:
271 {
272 sal_Int32 value = 0;
273 m_pStream->ReadInt32(value);
274 aData.push_back(Any(value));
275 }
276 break;
277 case DataType::BIGINT:
278 {
279 sal_Int64 value = 0;
280 m_pStream->ReadInt64(value);
281 aData.push_back(Any(value));
282 }
283 break;
284 case DataType::REAL:
285 case DataType::FLOAT:
286 case DataType::DOUBLE:
287 {
288 double value = 0;
289 m_pStream->ReadDouble(value);
290 // FIXME double is not necessarily 4 bytes
291 aData.push_back(Any(value));
292 }
293 break;
294 case DataType::NUMERIC:
295 case DataType::DECIMAL:
296 {
297 sal_Int32 nSize = 0;
298 m_pStream->ReadInt32(nSize);
299
300 std::vector<sal_uInt8> aBytes(nSize);
301 m_pStream->ReadBytes(aBytes.data(), nSize);
302 assert(aBytes.size() > 0);
303
304 sal_Int32 nScale = 0;
305 m_pStream->ReadInt32(nScale);
306
307 OUString sNum = lcl_makeStringFromBigint(std::move(aBytes));
308 Sequence<Any> result{ Any(lcl_putDot(sNum, nScale)), Any(nScale) };
309 aData.push_back(Any(result));
310 }
311 break;
312 case DataType::DATE:
313 {
314 sal_Int64 value = 0;
315 m_pStream->ReadInt64(value); // in millisec, from 1970
316 ptime epoch = time_from_string("1970-01-01 00:00:00.000");
317 ptime time = epoch + milliseconds(value);
318 date asDate = time.date();
319
320 css::util::Date loDate(asDate.day(), asDate.month(),
321 asDate.year()); // day, month, year
322 aData.push_back(Any(loDate));
323 }
324 break;
325 case DataType::TIME:
326 {
327 sal_Int64 value = 0;
328 m_pStream->ReadInt64(value);
329 auto valueInSecs = value / 1000;
330 /* Observed valueInSecs fall in the range from
331 negative one day to positive two days. Coerce
332 valueInSecs between zero and positive one day.*/
333 const int secPerDay = 24 * 60 * 60;
334 valueInSecs = (valueInSecs + secPerDay) % secPerDay;
335
336 auto nHours = valueInSecs / (60 * 60);
337 valueInSecs = valueInSecs % 3600;
338 const sal_uInt16 nMins = valueInSecs / 60;
339 const sal_uInt16 nSecs = valueInSecs % 60;
340 css::util::Time time((value % 1000) * 1000000, nSecs, nMins, nHours, true);
341 aData.push_back(Any(time));
342 }
343 break;
344 case DataType::TIMESTAMP:
345 {
346 sal_Int64 nEpochMillis = 0;
347 m_pStream->ReadInt64(nEpochMillis);
348 ptime epoch = time_from_string("1970-01-01 00:00:00.000");
349 ptime time = epoch + milliseconds(nEpochMillis);
350 date asDate = time.date();
351
352 sal_Int32 nNanos = 0;
353 m_pStream->ReadInt32(nNanos);
354
355 // convert into LO internal representation of dateTime
356 css::util::DateTime dateTime;
357 dateTime.NanoSeconds = nNanos;
358 dateTime.Seconds = time.time_of_day().seconds();
359 dateTime.Minutes = time.time_of_day().minutes();
360 dateTime.Hours = time.time_of_day().hours();
361 dateTime.Day = asDate.day();
362 dateTime.Month = asDate.month();
363 dateTime.Year = asDate.year();
364 aData.push_back(Any(dateTime));
365 }
366 break;
367 case DataType::BOOLEAN:
368 {
369 sal_uInt8 nBool = 0;
370 m_pStream->ReadUChar(nBool);
371 aData.push_back(Any(static_cast<bool>(nBool)));
372 }
373 break;
374 case DataType::OTHER:
375 aData.push_back(Any{}); // TODO
376 break;
377 case DataType::BINARY:
378 case DataType::VARBINARY:
379 case DataType::LONGVARBINARY:
380 {
381 sal_Int32 nSize = 0;
382 m_pStream->ReadInt32(nSize);
383
384 Sequence<sal_Int8> aBytes(nSize);
385 m_pStream->ReadBytes(aBytes.getArray(), nSize);
386 aData.push_back(Any(aBytes));
387 }
388 break;
389
390 default:
391 throw WrongFormatException();
392 }
393 }
394 return aData;
395}
396
397} // namespace dbahsql
398
399/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< css::uno::Any > readOneRow(const std::vector< ColumnDefinition > &colTypes)
Reads one row from the actual position.
std::unique_ptr< SvStream > m_pStream
OUString readUTF(sal_Int32 nLen)
SvStream * getInputStream() const
void setInputStream(css::uno::Reference< css::io::XInputStream > const &rStream)
void seek(sal_Int32 nPos)
Sets the file-pointer offset, measured from the beginning of the file.
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
Any value
int nCount
unsigned char byte
sal_uInt16 nPos
constexpr OUStringLiteral aData
int i
std::vector< sal_uInt8 > bytes
QPRO_FUNC_TYPE nType
unsigned char sal_uInt8
sal_uInt16 sal_Unicode
Any result
std::unique_ptr< char[]> aBuffer