1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <string_view>
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>
31 #include <unotools/ucbstreamhelper.hxx>
32 #include <tools/stream.hxx>
33 #include <rtl/ustrbuf.hxx>
35 #include <boost/date_time/posix_time/posix_time.hpp>
40 * Converts binary represented big integer value to BCD (Binary Coded
41 * Decimal), and returns a string representation of the number.
43 * Bytes[0] is the most significant part of the number.
45 OUString
lcl_double_dabble(const std::vector
<sal_uInt8
>& bytes
)
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);
51 for (size_t i
= 0; i
< bytes
.size(); ++i
)
53 for (size_t j
= 0; j
< 8; ++j
)
55 /* This bit will be shifted in on the right. */
56 int shifted_in
= (bytes
[i
] & (1 << (7 - j
))) ? 1 : 0;
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;
62 /* Shift scratch to the left by one position. */
63 for (size_t k
= 0; k
< nscratch
- 1; ++k
)
67 scratch
[k
] |= (scratch
[k
+ 1] >= 8) ? 1 : 0;
70 /* Shift in the new bit from arr. */
71 scratch
[nscratch
- 1] <<= 1;
72 scratch
[nscratch
- 1] &= 0xF;
73 scratch
[nscratch
- 1] |= shifted_in
;
77 auto it
= scratch
.begin();
78 /* Remove leading zeros from the scratch space. */
79 while (*it
== 0 && scratch
.size() > 1)
81 it
= scratch
.erase(it
);
84 /* Convert the scratch space from BCD digits to ASCII. */
85 for (auto& digit
: scratch
)
88 /* Resize and return the resulting string. */
89 return OStringToOUString(std::string_view(scratch
.data(), scratch
.size()),
90 RTL_TEXTENCODING_UTF8
);
93 OUString
lcl_makeStringFromBigint(std::vector
<sal_uInt8
>&& aBytes
)
98 if ((aBytes
[0] & 0x80) != 0)
101 for (auto& byte
: aBytes
)
103 // add 1 to byte array
104 // FIXME e.g. 10000 valid ?
105 for (size_t i
= aBytes
.size() - 1; i
!= 0; --i
)
112 // convert binary to BCD
113 OUString sNum
= lcl_double_dabble(aBytes
);
115 return sRet
.makeStringAndClear();
118 OUString
lcl_putDot(std::u16string_view sNum
, sal_Int32 nScale
)
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
)
127 sBuf
.insert(sBuf
.getLength() - 1 - nScale
, ".");
128 return sBuf
.makeStringAndClear();
134 using namespace css::uno
;
135 using namespace css::sdbc
;
136 using namespace css::io
;
137 using namespace boost::posix_time
;
138 using namespace boost::gregorian
;
140 HsqlRowInputStream::HsqlRowInputStream() {}
142 void HsqlRowInputStream::setInputStream(Reference
<XInputStream
> const& rStream
)
144 m_pStream
= utl::UcbStreamHelper::CreateStream(rStream
, true);
145 m_pStream
->SetEndian(SvStreamEndian::BIG
);
148 SvStream
* HsqlRowInputStream::getInputStream() const { return m_pStream
.get(); }
150 void HsqlRowInputStream::seek(sal_Int32 nPos
) { m_pStream
->Seek(nPos
); }
152 OUString
HsqlRowInputStream::readString()
155 m_pStream
->ReadInt32(nLen
);
156 return readUTF(nLen
);
159 OUString
HsqlRowInputStream::readUTF(sal_Int32 nUTFLen
)
161 Sequence
<sal_Unicode
> aBuffer(nUTFLen
);
162 sal_Unicode
* pStr
= aBuffer
.getArray();
164 sal_Int32 nCount
= 0;
165 sal_Int32 nStrLen
= 0;
166 while (nCount
< nUTFLen
)
169 m_pStream
->ReadUChar(c
);
170 sal_uInt8 char2
, char3
;
188 // 110x xxxx 10xx xxxx
190 if (nCount
> nUTFLen
)
192 throw WrongFormatException();
195 m_pStream
->ReadUChar(char2
);
196 if ((char2
& 0xC0) != 0x80)
198 throw WrongFormatException();
201 pStr
[nStrLen
++] = (sal_Unicode(c
& 0x1F) << 6) | (char2
& 0x3F);
205 // 1110 xxxx 10xx xxxx 10xx xxxx
207 if (nCount
> nUTFLen
)
209 throw WrongFormatException();
212 m_pStream
->ReadUChar(char2
);
213 m_pStream
->ReadUChar(char3
);
215 if (((char2
& 0xC0) != 0x80) || ((char3
& 0xC0) != 0x80))
217 throw WrongFormatException();
219 pStr
[nStrLen
++] = (sal_Unicode(c
& 0x0F) << 12) | (sal_Unicode(char2
& 0x3F) << 6)
224 // 10xx xxxx, 1111 xxxx
225 throw WrongFormatException();
228 return OUString(pStr
, nStrLen
);
231 bool HsqlRowInputStream::checkNull()
234 m_pStream
->ReadUChar(nNull
);
238 std::vector
<Any
> HsqlRowInputStream::readOneRow(const std::vector
<ColumnDefinition
>& nColTypes
)
240 auto nLen
= nColTypes
.size();
241 std::vector
<Any
> aData
;
243 for (size_t i
= 0; i
< nLen
; ++i
)
247 aData
.push_back(Any());
251 sal_Int32 nType
= nColTypes
[i
].getDataType();
253 // TODO throw error on EoF
258 case DataType::VARCHAR
:
259 case DataType::LONGVARCHAR
:
260 aData
.push_back(Any(readString()));
262 case DataType::TINYINT
:
263 case DataType::SMALLINT
:
266 m_pStream
->ReadInt16(value
);
267 aData
.push_back(Any(value
));
270 case DataType::INTEGER
:
273 m_pStream
->ReadInt32(value
);
274 aData
.push_back(Any(value
));
277 case DataType::BIGINT
:
280 m_pStream
->ReadInt64(value
);
281 aData
.push_back(Any(value
));
285 case DataType::FLOAT
:
286 case DataType::DOUBLE
:
289 m_pStream
->ReadDouble(value
);
290 // FIXME double is not necessarily 4 bytes
291 aData
.push_back(Any(value
));
294 case DataType::NUMERIC
:
295 case DataType::DECIMAL
:
298 m_pStream
->ReadInt32(nSize
);
300 std::vector
<sal_uInt8
> aBytes(nSize
);
301 m_pStream
->ReadBytes(aBytes
.data(), nSize
);
302 assert(aBytes
.size() > 0);
304 sal_Int32 nScale
= 0;
305 m_pStream
->ReadInt32(nScale
);
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
));
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();
320 css::util::Date
loDate(asDate
.day(), asDate
.month(),
321 asDate
.year()); // day, month, year
322 aData
.push_back(Any(loDate
));
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
;
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
));
344 case DataType::TIMESTAMP
:
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();
352 sal_Int32 nNanos
= 0;
353 m_pStream
->ReadInt32(nNanos
);
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
));
367 case DataType::BOOLEAN
:
370 m_pStream
->ReadUChar(nBool
);
371 aData
.push_back(Any(static_cast<bool>(nBool
)));
374 case DataType::OTHER
:
375 aData
.push_back(Any
{}); // TODO
377 case DataType::BINARY
:
378 case DataType::VARBINARY
:
379 case DataType::LONGVARBINARY
:
382 m_pStream
->ReadInt32(nSize
);
384 Sequence
<sal_Int8
> aBytes(nSize
);
385 m_pStream
->ReadBytes(aBytes
.getArray(), nSize
);
386 aData
.push_back(Any(aBytes
));
391 throw WrongFormatException();
397 } // namespace dbahsql
399 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */