blob: 4b9845da68a3f6c3d2f697f321d508f9c1ea0a4f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <string>
#include <sstream>
#include "ignite/common/bits.h"
#include "ignite/impl/binary/binary_utils.h"
#include "ignite/odbc/system/odbc_constants.h"
#include "ignite/odbc/app/application_data_buffer.h"
#include "ignite/odbc/utility.h"
#include "ignite/odbc/log.h"
namespace
{
// Just copy bytes currently.
// Only works for ASCII character set.
ignite::odbc::app::ConversionResult::Type StringToWstring(const char* str, int64_t strLen, SQLWCHAR* wstr, int64_t wstrLen)
{
using namespace ignite::odbc;
if (wstrLen <= 0)
return app::ConversionResult::AI_VARLEN_DATA_TRUNCATED;
int64_t toCopy = std::min(strLen, wstrLen - 1);
if (toCopy <= 0)
{
wstr[0] = 0;
return app::ConversionResult::AI_VARLEN_DATA_TRUNCATED;
}
for (int64_t i = 0; i < toCopy; ++i)
wstr[i] = str[i];
wstr[toCopy] = 0;
if (toCopy < strLen)
return app::ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return app::ConversionResult::AI_SUCCESS;
}
}
namespace ignite
{
namespace odbc
{
namespace app
{
using impl::binary::BinaryUtils;
ApplicationDataBuffer::ApplicationDataBuffer() :
type(type_traits::OdbcNativeType::AI_UNSUPPORTED),
buffer(0),
buflen(0),
reslen(0),
byteOffset(0),
elementOffset(0)
{
// No-op.
}
ApplicationDataBuffer::ApplicationDataBuffer(type_traits::OdbcNativeType::Type type,
void* buffer, SqlLen buflen, SqlLen* reslen) :
type(type),
buffer(buffer),
buflen(buflen),
reslen(reslen),
byteOffset(0),
elementOffset(0)
{
// No-op.
}
ApplicationDataBuffer::ApplicationDataBuffer(const ApplicationDataBuffer& other) :
type(other.type),
buffer(other.buffer),
buflen(other.buflen),
reslen(other.reslen),
byteOffset(other.byteOffset),
elementOffset(other.elementOffset)
{
// No-op.
}
ApplicationDataBuffer::~ApplicationDataBuffer()
{
// No-op.
}
ApplicationDataBuffer & ApplicationDataBuffer::operator=(const ApplicationDataBuffer & other)
{
type = other.type;
buffer = other.buffer;
buflen = other.buflen;
reslen = other.reslen;
byteOffset = other.byteOffset;
elementOffset = other.elementOffset;
return *this;
}
template<typename T>
ConversionResult::Type ApplicationDataBuffer::PutNum(T value)
{
using namespace type_traits;
LOG_MSG("value: " << value);
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
switch (type)
{
case OdbcNativeType::AI_SIGNED_TINYINT:
{
return PutNumToNumBuffer<signed char>(value);
}
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
{
return PutNumToNumBuffer<unsigned char>(value);
}
case OdbcNativeType::AI_SIGNED_SHORT:
{
return PutNumToNumBuffer<SQLSMALLINT>(value);
}
case OdbcNativeType::AI_UNSIGNED_SHORT:
{
return PutNumToNumBuffer<SQLUSMALLINT>(value);
}
case OdbcNativeType::AI_SIGNED_LONG:
{
return PutNumToNumBuffer<SQLINTEGER>(value);
}
case OdbcNativeType::AI_UNSIGNED_LONG:
{
return PutNumToNumBuffer<SQLUINTEGER>(value);
}
case OdbcNativeType::AI_SIGNED_BIGINT:
{
return PutNumToNumBuffer<SQLBIGINT>(value);
}
case OdbcNativeType::AI_UNSIGNED_BIGINT:
{
return PutNumToNumBuffer<SQLUBIGINT>(value);
}
case OdbcNativeType::AI_FLOAT:
{
return PutNumToNumBuffer<SQLREAL>(value);
}
case OdbcNativeType::AI_DOUBLE:
{
return PutNumToNumBuffer<SQLDOUBLE>(value);
}
case OdbcNativeType::AI_CHAR:
{
return PutValToStrBuffer<char>(value);
}
case OdbcNativeType::AI_WCHAR:
{
return PutValToStrBuffer<wchar_t>(value);
}
case OdbcNativeType::AI_NUMERIC:
{
if (dataPtr)
{
SQL_NUMERIC_STRUCT* out =
reinterpret_cast<SQL_NUMERIC_STRUCT*>(dataPtr);
uint64_t uval = static_cast<uint64_t>(value < 0 ? -value : value);
out->precision = common::bits::DigitLength(uval);
out->scale = 0;
out->sign = value < 0 ? 0 : 1;
memset(out->val, 0, SQL_MAX_NUMERIC_LEN);
memcpy(out->val, &uval, std::min<int>(SQL_MAX_NUMERIC_LEN, sizeof(uval)));
}
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_NUMERIC_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
{
if (dataPtr)
memcpy(dataPtr, &value, std::min(sizeof(value), static_cast<size_t>(buflen)));
if (resLenPtr)
*resLenPtr = sizeof(value);
return static_cast<size_t>(buflen) < sizeof(value) ?
ConversionResult::AI_VARLEN_DATA_TRUNCATED : ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TDATE:
{
return PutDate(Date(static_cast<int64_t>(value)));
}
case OdbcNativeType::AI_TTIMESTAMP:
{
return PutTimestamp(Timestamp(static_cast<int64_t>(value)));
}
case OdbcNativeType::AI_TTIME:
{
return PutTime(Time(static_cast<int64_t>(value)));
}
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
template<typename Tbuf, typename Tin>
ConversionResult::Type ApplicationDataBuffer::PutNumToNumBuffer(Tin value)
{
void* dataPtr = GetData();
SqlLen* resLenPtr = GetResLen();
if (dataPtr)
{
Tbuf* out = reinterpret_cast<Tbuf*>(dataPtr);
*out = static_cast<Tbuf>(value);
}
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(Tbuf));
return ConversionResult::AI_SUCCESS;
}
template<typename CharT, typename Tin>
ConversionResult::Type ApplicationDataBuffer::PutValToStrBuffer(const Tin& value)
{
typedef std::basic_stringstream<CharT> ConverterType;
ConverterType converter;
converter << value;
int32_t written = 0;
return PutStrToStrBuffer<CharT>(converter.str(), written);
}
template<typename CharT>
ConversionResult::Type ApplicationDataBuffer::PutValToStrBuffer(const int8_t& value)
{
typedef std::basic_stringstream<CharT> ConverterType;
ConverterType converter;
converter << static_cast<int>(value);
int32_t written = 0;
return PutStrToStrBuffer<CharT>(converter.str(), written);
}
template<typename OutCharT, typename InCharT>
ConversionResult::Type ApplicationDataBuffer::PutStrToStrBuffer(const std::basic_string<InCharT>& value,
int32_t& written)
{
written = 0;
SqlLen charSize = static_cast<SqlLen>(sizeof(OutCharT));
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(value.size());
if (!dataPtr)
return ConversionResult::AI_SUCCESS;
if (buflen < charSize)
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
OutCharT* out = reinterpret_cast<OutCharT*>(dataPtr);
SqlLen outLen = (buflen / charSize) - 1;
SqlLen toCopy = std::min<SqlLen>(outLen, value.size());
for (SqlLen i = 0; i < toCopy; ++i)
out[i] = value[i];
out[toCopy] = 0;
written = static_cast<int32_t>(toCopy);
if (toCopy < static_cast<SqlLen>(value.size()))
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
ConversionResult::Type ApplicationDataBuffer::PutRawDataToBuffer(void *data, size_t len, int32_t& written)
{
SqlLen iLen = static_cast<SqlLen>(len);
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
if (resLenPtr)
*resLenPtr = iLen;
SqlLen toCopy = std::min(buflen, iLen);
if (dataPtr != 0 && toCopy > 0)
memcpy(dataPtr, data, static_cast<size_t>(toCopy));
written = static_cast<int32_t>(toCopy);
return toCopy < iLen ? ConversionResult::AI_VARLEN_DATA_TRUNCATED : ConversionResult::AI_SUCCESS;
}
ConversionResult::Type ApplicationDataBuffer::PutInt8(int8_t value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutInt16(int16_t value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutInt32(int32_t value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutInt64(int64_t value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutFloat(float value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutDouble(double value)
{
return PutNum(value);
}
ConversionResult::Type ApplicationDataBuffer::PutString(const std::string & value)
{
int32_t written = 0;
return PutString(value, written);
}
ConversionResult::Type ApplicationDataBuffer::PutString(const std::string& value, int32_t& written)
{
using namespace type_traits;
LOG_MSG("value: " << value);
switch (type)
{
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
case OdbcNativeType::AI_NUMERIC:
{
std::stringstream converter;
converter << value;
int64_t numValue;
converter >> numValue;
written = static_cast<int32_t>(value.size());
return PutNum(numValue);
}
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
{
std::stringstream converter;
converter << value;
double numValue;
converter >> numValue;
written = static_cast<int32_t>(value.size());
return PutNum(numValue);
}
case OdbcNativeType::AI_CHAR:
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
{
return PutStrToStrBuffer<char>(value, written);
}
case OdbcNativeType::AI_WCHAR:
{
return PutStrToStrBuffer<wchar_t>(value, written);
}
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutGuid(const Guid& value)
{
using namespace type_traits;
LOG_MSG("value: " << value);
SqlLen* resLenPtr = GetResLen();
switch (type)
{
case OdbcNativeType::AI_CHAR:
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
{
return PutValToStrBuffer<char>(value);
}
case OdbcNativeType::AI_WCHAR:
{
return PutValToStrBuffer<wchar_t>(value);
}
case OdbcNativeType::AI_GUID:
{
SQLGUID* guid = reinterpret_cast<SQLGUID*>(GetData());
guid->Data1 = static_cast<uint32_t>(value.GetMostSignificantBits() >> 32);
guid->Data2 = static_cast<uint16_t>(value.GetMostSignificantBits() >> 16);
guid->Data3 = static_cast<uint16_t>(value.GetMostSignificantBits());
uint64_t lsb = value.GetLeastSignificantBits();
for (size_t i = 0; i < sizeof(guid->Data4); ++i)
guid->Data4[i] = (lsb >> (sizeof(guid->Data4) - i - 1) * 8) & 0xFF;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQLGUID));
return ConversionResult::AI_SUCCESS;
}
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutBinaryData(void *data, size_t len, int32_t& written)
{
using namespace type_traits;
switch (type)
{
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
{
return PutRawDataToBuffer(data, len, written);
}
case OdbcNativeType::AI_CHAR:
{
std::stringstream converter;
uint8_t *dataBytes = reinterpret_cast<uint8_t*>(data);
for (size_t i = 0; i < len; ++i)
{
converter << std::hex
<< std::setfill('0')
<< std::setw(2)
<< static_cast<unsigned>(dataBytes[i]);
}
return PutStrToStrBuffer<char>(converter.str(), written);
}
case OdbcNativeType::AI_WCHAR:
{
std::wstringstream converter;
uint8_t *dataBytes = reinterpret_cast<uint8_t*>(data);
for (size_t i = 0; i < len; ++i)
{
converter << std::hex
<< std::setfill(L'0')
<< std::setw(2)
<< static_cast<unsigned>(dataBytes[i]);
}
return PutStrToStrBuffer<wchar_t>(converter.str(), written);
}
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutNull()
{
SqlLen* resLenPtr = GetResLen();
if (!resLenPtr)
return ConversionResult::AI_INDICATOR_NEEDED;
*resLenPtr = SQL_NULL_DATA;
return ConversionResult::AI_SUCCESS;
}
ConversionResult::Type ApplicationDataBuffer::PutDecimal(const common::Decimal& value)
{
using namespace type_traits;
SqlLen* resLenPtr = GetResLen();
switch (type)
{
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
{
PutNum<int64_t>(value.ToInt64());
return ConversionResult::AI_FRACTIONAL_TRUNCATED;
}
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
{
PutNum<double>(value.ToDouble());
return ConversionResult::AI_FRACTIONAL_TRUNCATED;
}
case OdbcNativeType::AI_CHAR:
case OdbcNativeType::AI_WCHAR:
{
std::stringstream converter;
converter << value;
int32_t dummy = 0;
return PutString(converter.str(), dummy);
}
case OdbcNativeType::AI_NUMERIC:
{
SQL_NUMERIC_STRUCT* numeric =
reinterpret_cast<SQL_NUMERIC_STRUCT*>(GetData());
common::Decimal zeroScaled;
value.SetScale(0, zeroScaled);
common::FixedSizeArray<int8_t> bytesBuffer;
const common::BigInteger& unscaled = zeroScaled.GetUnscaledValue();
unscaled.MagnitudeToBytes(bytesBuffer);
for (int32_t i = 0; i < SQL_MAX_NUMERIC_LEN; ++i)
{
int32_t bufIdx = bytesBuffer.GetSize() - 1 - i;
if (bufIdx >= 0)
numeric->val[i] = bytesBuffer[bufIdx];
else
numeric->val[i] = 0;
}
numeric->scale = 0;
numeric->sign = unscaled.GetSign() < 0 ? 0 : 1;
numeric->precision = unscaled.GetPrecision();
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_NUMERIC_STRUCT));
if (bytesBuffer.GetSize() > SQL_MAX_NUMERIC_LEN)
return ConversionResult::AI_FRACTIONAL_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_BINARY:
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutDate(const Date& value)
{
using namespace type_traits;
tm tmTime;
common::DateToCTm(value, tmTime);
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
char* buffer = reinterpret_cast<char*>(dataPtr);
const size_t valLen = sizeof("HHHH-MM-DD") - 1;
if (resLenPtr)
*resLenPtr = valLen;
if (buffer)
strftime(buffer, GetSize(), "%Y-%m-%d", &tmTime);
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_WCHAR:
{
SQLWCHAR* buffer = reinterpret_cast<SQLWCHAR*>(dataPtr);
const size_t valLen = sizeof("HHHH-MM-DD") - 1;
if (resLenPtr)
*resLenPtr = valLen;
if (buffer)
{
std::string tmp(valLen + 1, 0);
strftime(&tmp[0], tmp.size(), "%Y-%m-%d", &tmTime);
StringToWstring(&tmp[0], tmp.size(), buffer, GetSize());
}
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TDATE:
{
SQL_DATE_STRUCT* buffer = reinterpret_cast<SQL_DATE_STRUCT*>(dataPtr);
buffer->year = tmTime.tm_year + 1900;
buffer->month = tmTime.tm_mon + 1;
buffer->day = tmTime.tm_mday;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_DATE_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TTIME:
{
SQL_TIME_STRUCT* buffer = reinterpret_cast<SQL_TIME_STRUCT*>(dataPtr);
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIME_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT*>(dataPtr);
buffer->year = tmTime.tm_year + 1900;
buffer->month = tmTime.tm_mon + 1;
buffer->day = tmTime.tm_mday;
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
buffer->fraction = 0;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIMESTAMP_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
case OdbcNativeType::AI_NUMERIC:
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutTimestamp(const Timestamp& value)
{
using namespace type_traits;
tm tmTime;
common::TimestampToCTm(value, tmTime);
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
const size_t valLen = sizeof("HHHH-MM-DD HH:MM:SS") - 1;
if (resLenPtr)
*resLenPtr = valLen;
char* buffer = reinterpret_cast<char*>(dataPtr);
if (buffer)
strftime(buffer, GetSize(), "%Y-%m-%d %H:%M:%S", &tmTime);
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_WCHAR:
{
const size_t valLen = sizeof("HHHH-MM-DD HH:MM:SS") - 1;
if (resLenPtr)
*resLenPtr = valLen;
SQLWCHAR* buffer = reinterpret_cast<SQLWCHAR*>(dataPtr);
if (buffer)
{
std::string tmp(GetSize(), 0);
strftime(&tmp[0], GetSize(), "%Y-%m-%d %H:%M:%S", &tmTime);
StringToWstring(&tmp[0], tmp.size(), buffer, GetSize());
}
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TDATE:
{
SQL_DATE_STRUCT* buffer = reinterpret_cast<SQL_DATE_STRUCT*>(dataPtr);
buffer->year = tmTime.tm_year + 1900;
buffer->month = tmTime.tm_mon + 1;
buffer->day = tmTime.tm_mday;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_DATE_STRUCT));
return ConversionResult::AI_FRACTIONAL_TRUNCATED;
}
case OdbcNativeType::AI_TTIME:
{
SQL_TIME_STRUCT* buffer = reinterpret_cast<SQL_TIME_STRUCT*>(dataPtr);
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIME_STRUCT));
return ConversionResult::AI_FRACTIONAL_TRUNCATED;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT*>(dataPtr);
buffer->year = tmTime.tm_year + 1900;
buffer->month = tmTime.tm_mon + 1;
buffer->day = tmTime.tm_mday;
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
buffer->fraction = value.GetSecondFraction();
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIMESTAMP_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
case OdbcNativeType::AI_NUMERIC:
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
ConversionResult::Type ApplicationDataBuffer::PutTime(const Time& value)
{
using namespace type_traits;
tm tmTime;
common::TimeToCTm(value, tmTime);
SqlLen* resLenPtr = GetResLen();
void* dataPtr = GetData();
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
const size_t valLen = sizeof("HH:MM:SS") - 1;
if (resLenPtr)
*resLenPtr = sizeof("HH:MM:SS") - 1;
char* buffer = reinterpret_cast<char*>(dataPtr);
if (buffer)
strftime(buffer, GetSize(), "%H:%M:%S", &tmTime);
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_WCHAR:
{
const size_t valLen = sizeof("HH:MM:SS") - 1;
if (resLenPtr)
*resLenPtr = sizeof("HH:MM:SS") - 1;
SQLWCHAR* buffer = reinterpret_cast<SQLWCHAR*>(dataPtr);
if (buffer)
{
std::string tmp(GetSize(), 0);
strftime(&tmp[0], GetSize(), "%H:%M:%S", &tmTime);
StringToWstring(&tmp[0], tmp.size(), buffer, GetSize());
}
if (static_cast<SqlLen>(valLen) + 1 > GetSize())
return ConversionResult::AI_VARLEN_DATA_TRUNCATED;
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TTIME:
{
SQL_TIME_STRUCT* buffer = reinterpret_cast<SQL_TIME_STRUCT*>(dataPtr);
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIME_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT*>(dataPtr);
buffer->year = tmTime.tm_year + 1900;
buffer->month = tmTime.tm_mon + 1;
buffer->day = tmTime.tm_mday;
buffer->hour = tmTime.tm_hour;
buffer->minute = tmTime.tm_min;
buffer->second = tmTime.tm_sec;
buffer->fraction = 0;
if (resLenPtr)
*resLenPtr = static_cast<SqlLen>(sizeof(SQL_TIMESTAMP_STRUCT));
return ConversionResult::AI_SUCCESS;
}
case OdbcNativeType::AI_BINARY:
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
case OdbcNativeType::AI_NUMERIC:
case OdbcNativeType::AI_TDATE:
default:
break;
}
return ConversionResult::AI_UNSUPPORTED_CONVERSION;
}
std::string ApplicationDataBuffer::GetString(size_t maxLen) const
{
using namespace type_traits;
std::string res;
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
size_t paramLen = GetInputSize();
if (!paramLen)
break;
res = utility::SqlStringToString(
reinterpret_cast<const unsigned char*>(GetData()), static_cast<int32_t>(paramLen));
if (res.size() > maxLen)
res.resize(maxLen);
break;
}
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
{
std::stringstream converter;
converter << GetNum<int64_t>();
res = converter.str();
break;
}
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
{
std::stringstream converter;
converter << GetNum<uint64_t>();
res = converter.str();
break;
}
case OdbcNativeType::AI_FLOAT:
{
std::stringstream converter;
converter << GetNum<float>();
res = converter.str();
break;
}
case OdbcNativeType::AI_NUMERIC:
case OdbcNativeType::AI_DOUBLE:
{
std::stringstream converter;
converter << GetNum<double>();
res = converter.str();
break;
}
default:
break;
}
return res;
}
int8_t ApplicationDataBuffer::GetInt8() const
{
return GetNum<int8_t>();
}
int16_t ApplicationDataBuffer::GetInt16() const
{
return GetNum<int16_t>();
}
int32_t ApplicationDataBuffer::GetInt32() const
{
return GetNum<int32_t>();
}
int64_t ApplicationDataBuffer::GetInt64() const
{
return GetNum<int64_t>();
}
float ApplicationDataBuffer::GetFloat() const
{
return GetNum<float>();
}
double ApplicationDataBuffer::GetDouble() const
{
return GetNum<double>();
}
Guid ApplicationDataBuffer::GetGuid() const
{
using namespace type_traits;
Guid res;
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = utility::SqlStringToString(
reinterpret_cast<const unsigned char*>(GetData()), static_cast<int32_t>(paramLen));
std::stringstream converter;
converter << str;
converter >> res;
break;
}
case OdbcNativeType::AI_GUID:
{
const SQLGUID* guid = reinterpret_cast<const SQLGUID*>(GetData());
uint64_t msb = static_cast<uint64_t>(guid->Data1) << 32 |
static_cast<uint64_t>(guid->Data2) << 16 |
static_cast<uint64_t>(guid->Data3);
uint64_t lsb = 0;
for (size_t i = 0; i < sizeof(guid->Data4); ++i)
lsb = guid->Data4[i] << (sizeof(guid->Data4) - i - 1) * 8;
res = Guid(msb, lsb);
break;
}
default:
break;
}
return res;
}
const void* ApplicationDataBuffer::GetData() const
{
return ApplyOffset(buffer, GetElementSize());
}
const SqlLen* ApplicationDataBuffer::GetResLen() const
{
return ApplyOffset(reslen, sizeof(*reslen));
}
void* ApplicationDataBuffer::GetData()
{
return ApplyOffset(buffer, GetElementSize());
}
SqlLen* ApplicationDataBuffer::GetResLen()
{
return ApplyOffset(reslen, sizeof(*reslen));
}
template<typename T>
T ApplicationDataBuffer::GetNum() const
{
using namespace type_traits;
T res = T();
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = GetString(paramLen);
std::stringstream converter;
converter << str;
// Workaround for char types which are recognised as
// symbolyc types and not numeric types.
if (sizeof(T) == 1)
{
short tmp;
converter >> tmp;
res = static_cast<T>(tmp);
}
else
converter >> res;
break;
}
case OdbcNativeType::AI_SIGNED_TINYINT:
{
res = static_cast<T>(*reinterpret_cast<const signed char*>(GetData()));
break;
}
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
{
res = static_cast<T>(*reinterpret_cast<const unsigned char*>(GetData()));
break;
}
case OdbcNativeType::AI_SIGNED_SHORT:
{
res = static_cast<T>(*reinterpret_cast<const signed short*>(GetData()));
break;
}
case OdbcNativeType::AI_UNSIGNED_SHORT:
{
res = static_cast<T>(*reinterpret_cast<const unsigned short*>(GetData()));
break;
}
case OdbcNativeType::AI_SIGNED_LONG:
{
res = static_cast<T>(*reinterpret_cast<const signed long*>(GetData()));
break;
}
case OdbcNativeType::AI_UNSIGNED_LONG:
{
res = static_cast<T>(*reinterpret_cast<const unsigned long*>(GetData()));
break;
}
case OdbcNativeType::AI_SIGNED_BIGINT:
{
res = static_cast<T>(*reinterpret_cast<const int64_t*>(GetData()));
break;
}
case OdbcNativeType::AI_UNSIGNED_BIGINT:
{
res = static_cast<T>(*reinterpret_cast<const uint64_t*>(GetData()));
break;
}
case OdbcNativeType::AI_FLOAT:
{
res = static_cast<T>(*reinterpret_cast<const float*>(GetData()));
break;
}
case OdbcNativeType::AI_DOUBLE:
{
res = static_cast<T>(*reinterpret_cast<const double*>(GetData()));
break;
}
case OdbcNativeType::AI_NUMERIC:
{
const SQL_NUMERIC_STRUCT* numeric =
reinterpret_cast<const SQL_NUMERIC_STRUCT*>(GetData());
common::Decimal dec(reinterpret_cast<const int8_t*>(numeric->val),
SQL_MAX_NUMERIC_LEN, numeric->scale, numeric->sign ? 1 : -1, false);
res = static_cast<T>(dec.ToInt64());
break;
}
default:
break;
}
return res;
}
Date ApplicationDataBuffer::GetDate() const
{
using namespace type_traits;
tm tmTime = { 0 };
switch (type)
{
case OdbcNativeType::AI_TDATE:
{
const SQL_DATE_STRUCT* buffer = reinterpret_cast<const SQL_DATE_STRUCT*>(GetData());
tmTime.tm_year = buffer->year - 1900;
tmTime.tm_mon = buffer->month - 1;
tmTime.tm_mday = buffer->day;
break;
}
case OdbcNativeType::AI_TTIME:
{
const SQL_TIME_STRUCT* buffer = reinterpret_cast<const SQL_TIME_STRUCT*>(GetData());
tmTime.tm_year = 70;
tmTime.tm_mday = 1;
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
break;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
const SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT*>(GetData());
tmTime.tm_year = buffer->year - 1900;
tmTime.tm_mon = buffer->month - 1;
tmTime.tm_mday = buffer->day;
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
break;
}
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = utility::SqlStringToString(
reinterpret_cast<const unsigned char*>(GetData()), static_cast<int32_t>(paramLen));
sscanf(str.c_str(), "%d-%d-%d %d:%d:%d", &tmTime.tm_year, &tmTime.tm_mon,
&tmTime.tm_mday, &tmTime.tm_hour, &tmTime.tm_min, &tmTime.tm_sec);
tmTime.tm_year = tmTime.tm_year - 1900;
tmTime.tm_mon = tmTime.tm_mon - 1;
break;
}
default:
break;
}
return common::CTmToDate(tmTime);
}
Timestamp ApplicationDataBuffer::GetTimestamp() const
{
using namespace type_traits;
tm tmTime = { 0 };
int32_t nanos = 0;
switch (type)
{
case OdbcNativeType::AI_TDATE:
{
const SQL_DATE_STRUCT* buffer = reinterpret_cast<const SQL_DATE_STRUCT*>(GetData());
tmTime.tm_year = buffer->year - 1900;
tmTime.tm_mon = buffer->month - 1;
tmTime.tm_mday = buffer->day;
break;
}
case OdbcNativeType::AI_TTIME:
{
const SQL_TIME_STRUCT* buffer = reinterpret_cast<const SQL_TIME_STRUCT*>(GetData());
tmTime.tm_year = 70;
tmTime.tm_mday = 1;
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
break;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
const SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT*>(GetData());
tmTime.tm_year = buffer->year - 1900;
tmTime.tm_mon = buffer->month - 1;
tmTime.tm_mday = buffer->day;
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
nanos = buffer->fraction;
break;
}
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = utility::SqlStringToString(
reinterpret_cast<const unsigned char*>(GetData()), static_cast<int32_t>(paramLen));
sscanf(str.c_str(), "%d-%d-%d %d:%d:%d", &tmTime.tm_year, &tmTime.tm_mon,
&tmTime.tm_mday, &tmTime.tm_hour, &tmTime.tm_min, &tmTime.tm_sec);
tmTime.tm_year = tmTime.tm_year - 1900;
tmTime.tm_mon = tmTime.tm_mon - 1;
break;
}
default:
break;
}
return common::CTmToTimestamp(tmTime, nanos);
}
Time ApplicationDataBuffer::GetTime() const
{
using namespace type_traits;
tm tmTime = { 0 };
tmTime.tm_year = 70;
tmTime.tm_mon = 0;
tmTime.tm_mday = 1;
switch (type)
{
case OdbcNativeType::AI_TTIME:
{
const SQL_TIME_STRUCT* buffer = reinterpret_cast<const SQL_TIME_STRUCT*>(GetData());
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
break;
}
case OdbcNativeType::AI_TTIMESTAMP:
{
const SQL_TIMESTAMP_STRUCT* buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT*>(GetData());
tmTime.tm_hour = buffer->hour;
tmTime.tm_min = buffer->minute;
tmTime.tm_sec = buffer->second;
break;
}
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = utility::SqlStringToString(
reinterpret_cast<const unsigned char*>(GetData()), static_cast<int32_t>(paramLen));
sscanf(str.c_str(), "%d:%d:%d", &tmTime.tm_hour, &tmTime.tm_min, &tmTime.tm_sec);
break;
}
default:
break;
}
return common::CTmToTime(tmTime);
}
void ApplicationDataBuffer::GetDecimal(common::Decimal& val) const
{
using namespace type_traits;
switch (type)
{
case OdbcNativeType::AI_CHAR:
{
SqlLen paramLen = GetInputSize();
if (!paramLen)
break;
std::string str = GetString(paramLen);
std::stringstream converter;
converter << str;
converter >> val;
break;
}
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_SIGNED_BIGINT:
{
val.AssignInt64(GetNum<int64_t>());
break;
}
case OdbcNativeType::AI_UNSIGNED_TINYINT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
{
val.AssignUint64(GetNum<uint64_t>());
break;
}
case OdbcNativeType::AI_FLOAT:
case OdbcNativeType::AI_DOUBLE:
{
val.AssignDouble(GetNum<double>());
break;
}
case OdbcNativeType::AI_NUMERIC:
{
const SQL_NUMERIC_STRUCT* numeric =
reinterpret_cast<const SQL_NUMERIC_STRUCT*>(GetData());
common::Decimal dec(reinterpret_cast<const int8_t*>(numeric->val),
SQL_MAX_NUMERIC_LEN, numeric->scale, numeric->sign ? 1 : -1, false);
val.Swap(dec);
break;
}
default:
{
val.AssignInt64(0);
break;
}
}
}
template<typename T>
T* ApplicationDataBuffer::ApplyOffset(T* ptr, size_t elemSize) const
{
if (!ptr)
return ptr;
return utility::GetPointerWithOffset(ptr, byteOffset + elemSize * elementOffset);
}
bool ApplicationDataBuffer::IsDataAtExec() const
{
const SqlLen* resLenPtr = GetResLen();
if (!resLenPtr)
return false;
int32_t ilen = static_cast<int32_t>(*resLenPtr);
return ilen <= SQL_LEN_DATA_AT_EXEC_OFFSET || ilen == SQL_DATA_AT_EXEC;
}
SqlLen ApplicationDataBuffer::GetDataAtExecSize() const
{
using namespace type_traits;
switch (type)
{
case OdbcNativeType::AI_WCHAR:
case OdbcNativeType::AI_CHAR:
case OdbcNativeType::AI_BINARY:
{
const SqlLen* resLenPtr = GetResLen();
if (!resLenPtr)
return 0;
int32_t ilen = static_cast<int32_t>(*resLenPtr);
if (ilen <= SQL_LEN_DATA_AT_EXEC_OFFSET)
ilen = SQL_LEN_DATA_AT_EXEC(ilen);
else
ilen = 0;
if (type == OdbcNativeType::AI_WCHAR)
ilen *= 2;
return ilen;
}
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
return static_cast<SqlLen>(sizeof(short));
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
return static_cast<SqlLen>(sizeof(long));
case OdbcNativeType::AI_FLOAT:
return static_cast<SqlLen>(sizeof(float));
case OdbcNativeType::AI_DOUBLE:
return static_cast<SqlLen>(sizeof(double));
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
return static_cast<SqlLen>(sizeof(char));
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
return static_cast<SqlLen>(sizeof(SQLBIGINT));
case OdbcNativeType::AI_TDATE:
return static_cast<SqlLen>(sizeof(SQL_DATE_STRUCT));
case OdbcNativeType::AI_TTIME:
return static_cast<SqlLen>(sizeof(SQL_TIME_STRUCT));
case OdbcNativeType::AI_TTIMESTAMP:
return static_cast<SqlLen>(sizeof(SQL_TIMESTAMP_STRUCT));
case OdbcNativeType::AI_NUMERIC:
return static_cast<SqlLen>(sizeof(SQL_NUMERIC_STRUCT));
case OdbcNativeType::AI_GUID:
return static_cast<SqlLen>(sizeof(SQLGUID));
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_UNSUPPORTED:
default:
break;
}
return 0;
}
SqlLen ApplicationDataBuffer::GetElementSize() const
{
using namespace type_traits;
switch (type)
{
case OdbcNativeType::AI_WCHAR:
case OdbcNativeType::AI_CHAR:
case OdbcNativeType::AI_BINARY:
return buflen;
case OdbcNativeType::AI_SIGNED_SHORT:
case OdbcNativeType::AI_UNSIGNED_SHORT:
return static_cast<SqlLen>(sizeof(short));
case OdbcNativeType::AI_SIGNED_LONG:
case OdbcNativeType::AI_UNSIGNED_LONG:
return static_cast<SqlLen>(sizeof(long));
case OdbcNativeType::AI_FLOAT:
return static_cast<SqlLen>(sizeof(float));
case OdbcNativeType::AI_DOUBLE:
return static_cast<SqlLen>(sizeof(double));
case OdbcNativeType::AI_BIT:
case OdbcNativeType::AI_SIGNED_TINYINT:
case OdbcNativeType::AI_UNSIGNED_TINYINT:
return static_cast<SqlLen>(sizeof(char));
case OdbcNativeType::AI_SIGNED_BIGINT:
case OdbcNativeType::AI_UNSIGNED_BIGINT:
return static_cast<SqlLen>(sizeof(SQLBIGINT));
case OdbcNativeType::AI_TDATE:
return static_cast<SqlLen>(sizeof(SQL_DATE_STRUCT));
case OdbcNativeType::AI_TTIME:
return static_cast<SqlLen>(sizeof(SQL_TIME_STRUCT));
case OdbcNativeType::AI_TTIMESTAMP:
return static_cast<SqlLen>(sizeof(SQL_TIMESTAMP_STRUCT));
case OdbcNativeType::AI_NUMERIC:
return static_cast<SqlLen>(sizeof(SQL_NUMERIC_STRUCT));
case OdbcNativeType::AI_GUID:
return static_cast<SqlLen>(sizeof(SQLGUID));
case OdbcNativeType::AI_DEFAULT:
case OdbcNativeType::AI_UNSUPPORTED:
default:
break;
}
return 0;
}
SqlLen ApplicationDataBuffer::GetInputSize() const
{
if (!IsDataAtExec())
{
const SqlLen *len = GetResLen();
return len ? *len : SQL_DEFAULT_PARAM;
}
return GetDataAtExecSize();
}
}
}
}