Skip to content

Commit

Permalink
Merge pull request #270 from traceon/delay-conversions
Browse files Browse the repository at this point in the history
Delay conversions during data fetching from the data source
  • Loading branch information
Enmk authored Mar 2, 2020
2 parents 374ab58 + eee18fe commit d783b44
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 30 deletions.
13 changes: 12 additions & 1 deletion driver/format/ODBCDriver2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,14 @@ void ODBCDriver2ResultSet::readValue(Field & dest, ColumnInfo & column_info) {
if (column_info.display_size_so_far < value.size())
column_info.display_size_so_far = value.size();

switch (column_info.type_without_parameters_id) {
constexpr bool convert_on_fetch_conservatively = true;

if (convert_on_fetch_conservatively) switch (column_info.type_without_parameters_id) {
case DataSourceTypeId::FixedString: readValueAs<DataSourceType< DataSourceTypeId::FixedString >>(value, dest, column_info); break;
case DataSourceTypeId::String: readValueAs<DataSourceType< DataSourceTypeId::String >>(value, dest, column_info); break;
default: readValueAs<WireTypeAnyAsString >(value, dest, column_info); break;
}
else switch (column_info.type_without_parameters_id) {
case DataSourceTypeId::Date: readValueAs<DataSourceType< DataSourceTypeId::Date >>(value, dest, column_info); break;
case DataSourceTypeId::DateTime: readValueAs<DataSourceType< DataSourceTypeId::DateTime >>(value, dest, column_info); break;
case DataSourceTypeId::Decimal: readValueAs<DataSourceType< DataSourceTypeId::Decimal >>(value, dest, column_info); break;
Expand Down Expand Up @@ -144,6 +151,10 @@ void ODBCDriver2ResultSet::readValue(Field & dest, ColumnInfo & column_info) {
string_pool.put(std::move(value));
}

void ODBCDriver2ResultSet::readValue(std::string & src, WireTypeAnyAsString & dest, ColumnInfo & column_info) {
dest.value = std::move(src);
}

void ODBCDriver2ResultSet::readValue(std::string & src, DataSourceType<DataSourceTypeId::Date> & dest, ColumnInfo & column_info) {
return value_manip::from_value<std::string>::template to_value<DataSourceType<DataSourceTypeId::Date>>::convert(src, dest);
}
Expand Down
2 changes: 2 additions & 0 deletions driver/format/ODBCDriver2.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class ODBCDriver2ResultSet
dest.data = std::move(value);
}

void readValue(std::string & src, WireTypeAnyAsString & dest, ColumnInfo & column_info);

void readValue(std::string & src, DataSourceType< DataSourceTypeId::Date > & dest, ColumnInfo & column_info);
void readValue(std::string & src, DataSourceType< DataSourceTypeId::DateTime > & dest, ColumnInfo & column_info);
void readValue(std::string & src, DataSourceType< DataSourceTypeId::Decimal > & dest, ColumnInfo & column_info);
Expand Down
43 changes: 21 additions & 22 deletions driver/format/RowBinaryWithNamesAndTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ void RowBinaryWithNamesAndTypesResultSet::readValue(Field & dest, ColumnInfo & c
}
}

constexpr bool convert_on_fetch_conservatively = true;

if (convert_on_fetch_conservatively) switch (column_info.type_without_parameters_id) {
case DataSourceTypeId::Date: return readValueAs<WireTypeDateAsInt >(dest, column_info);
case DataSourceTypeId::DateTime: return readValueAs<WireTypeDateTimeAsInt>(dest, column_info);
default: break; // Continue with the next complete switch...
}

switch (column_info.type_without_parameters_id) {
case DataSourceTypeId::Date: return readValueAs<DataSourceType< DataSourceTypeId::Date >>(dest, column_info);
case DataSourceTypeId::DateTime: return readValueAs<DataSourceType< DataSourceTypeId::DateTime >>(dest, column_info);
Expand All @@ -138,33 +146,24 @@ void RowBinaryWithNamesAndTypesResultSet::readValue(Field & dest, ColumnInfo & c
}
}

void RowBinaryWithNamesAndTypesResultSet::readValue(DataSourceType<DataSourceTypeId::Date> & dest, ColumnInfo & column_info) {
std::uint16_t days_since_epoch = 0;
readPOD(days_since_epoch);
void RowBinaryWithNamesAndTypesResultSet::readValue(WireTypeDateAsInt & dest, ColumnInfo & column_info) {
readPOD(dest.value);
}

std::time_t time = days_since_epoch;
time = time * 24 * 60 * 60; // Now it's seconds since epoch.
const auto & tm = *std::localtime(&time);
void RowBinaryWithNamesAndTypesResultSet::readValue(WireTypeDateTimeAsInt & dest, ColumnInfo & column_info) {
readPOD(dest.value);
}

dest.value.year = 1900 + tm.tm_year;
dest.value.month = 1 + tm.tm_mon;
dest.value.day = tm.tm_mday;
void RowBinaryWithNamesAndTypesResultSet::readValue(DataSourceType<DataSourceTypeId::Date> & dest, ColumnInfo & column_info) {
WireTypeDateAsInt dest_raw;
readValue(dest_raw, column_info);
value_manip::from_value<decltype(dest_raw)>::template to_value<decltype(dest)>::convert(dest_raw, dest);
}

void RowBinaryWithNamesAndTypesResultSet::readValue(DataSourceType<DataSourceTypeId::DateTime> & dest, ColumnInfo & column_info) {
std::uint32_t secs_since_epoch = 0;
readPOD(secs_since_epoch);

std::time_t time = secs_since_epoch;
const auto & tm = *std::localtime(&time);

dest.value.year = 1900 + tm.tm_year;
dest.value.month = 1 + tm.tm_mon;
dest.value.day = tm.tm_mday;
dest.value.hour = tm.tm_hour;
dest.value.minute = tm.tm_min;
dest.value.second = tm.tm_sec;
dest.value.fraction = 0;
WireTypeDateTimeAsInt dest_raw;
readValue(dest_raw, column_info);
value_manip::from_value<decltype(dest_raw)>::template to_value<decltype(dest)>::convert(dest_raw, dest);
}

void RowBinaryWithNamesAndTypesResultSet::readValue(DataSourceType<DataSourceTypeId::Decimal> & dest, ColumnInfo & column_info) {
Expand Down
3 changes: 3 additions & 0 deletions driver/format/RowBinaryWithNamesAndTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class RowBinaryWithNamesAndTypesResultSet
dest.data = std::move(value);
}

void readValue(WireTypeDateAsInt & dest, ColumnInfo & column_info);
void readValue(WireTypeDateTimeAsInt & dest, ColumnInfo & column_info);

void readValue(DataSourceType< DataSourceTypeId::Date > & dest, ColumnInfo & column_info);
void readValue(DataSourceType< DataSourceTypeId::DateTime > & dest, ColumnInfo & column_info);
void readValue(DataSourceType< DataSourceTypeId::Decimal > & dest, ColumnInfo & column_info);
Expand Down
7 changes: 6 additions & 1 deletion driver/result_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ class Field {
DataSourceType< DataSourceTypeId::UInt16 >,
DataSourceType< DataSourceTypeId::UInt32 >,
DataSourceType< DataSourceTypeId::UInt64 >,
DataSourceType< DataSourceTypeId::UUID >
DataSourceType< DataSourceTypeId::UUID >,

// In case we approach value conversion conservatively...
WireTypeAnyAsString,
WireTypeDateAsInt,
WireTypeDateTimeAsInt
>;

SQLRETURN extract(BindingInfo & binding_info) const;
Expand Down
11 changes: 6 additions & 5 deletions driver/test/statement_parameters_it.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,8 @@ TEST_P(ParameterColumnRoundTripDecimalAsStringSymmetric, Execute) {
INSTANTIATE_TEST_SUITE_P(TypeConversion, ParameterColumnRoundTripDecimalAsStringSymmetric,
::testing::Values(

// TODO: do DECIMALs have to not start with dot?
// TODO: add cases with 0 whole part. Currently the unified testing doesn't play well with the
// different wire formats with enabled conservative value conversions.

"0",
"12345",
Expand All @@ -615,11 +616,11 @@ INSTANTIATE_TEST_SUITE_P(TypeConversion, ParameterColumnRoundTripDecimalAsString
"12345.001002003000",
"100000000000000000",
"-100000000000000000",
".000000000000000001",
"-.000000000000000001",
"1.00000000000000001",
"-1.00000000000000001",
"999999999999999999",
"-999999999999999999",
".999999999999999999",
"-.999999999999999999"
"1.99999999999999999",
"-1.99999999999999999"
)
);
97 changes: 96 additions & 1 deletion driver/utils/type_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ inline SQLRETURN fillOutputString(
);
}

// ObjectType, that is a pointer type, is treated as an integer, the value of that pointer.
// If ObjectType is a pointer type then obj is treated as an integer corrsponding to the value of that pointer itself.
template <typename ObjectType, typename LengthType1, typename LengthType2>
inline SQLRETURN fillOutputPOD(
const ObjectType & obj,
Expand Down Expand Up @@ -406,6 +406,27 @@ struct SimpleTypeWrapper {
T value;
};

// Values stored exactly as they are written on wire in ODBCDriver2 format.
struct WireTypeAnyAsString
: public SimpleTypeWrapper<std::string>
{
using SimpleTypeWrapper<std::string>::SimpleTypeWrapper;
};

// Date stored exactly as it is represented on wire in RowBinaryWithNamesAndTypes format.
struct WireTypeDateAsInt
: public SimpleTypeWrapper<std::uint16_t>
{
using SimpleTypeWrapper<std::uint16_t>::SimpleTypeWrapper;
};

// DateTime stored exactly as it is represented on wire in RowBinaryWithNamesAndTypes format.
struct WireTypeDateTimeAsInt
: public SimpleTypeWrapper<std::uint32_t>
{
using SimpleTypeWrapper<std::uint32_t>::SimpleTypeWrapper;
};

template <DataSourceTypeId Id> struct DataSourceType; // Leave unimplemented for general case.

template <>
Expand Down Expand Up @@ -565,6 +586,11 @@ template <> struct is_string_data_source_type<DataSourceType<DataSourceTypeId::F
{
};

template <> struct is_string_data_source_type<WireTypeAnyAsString>
: public std::true_type
{
};

template <class T> inline constexpr bool is_string_data_source_type_v = is_string_data_source_type<T>::value;

// Used to avoid duplicate specializations in platforms where 'std::int32_t' or 'std::int64_t' are typedef'd as 'long'.
Expand Down Expand Up @@ -1720,6 +1746,75 @@ namespace value_manip {
};
};

template <>
struct from_value<WireTypeAnyAsString> {
using SourceType = WireTypeAnyAsString;

template <typename DestinationType>
struct to_value {
static inline void convert(const SourceType & src, DestinationType & dest) {
return from_value<std::string>::template to_value<DestinationType>::convert(src.value, dest);
}
};
};

template <>
struct from_value<WireTypeDateAsInt> {
using SourceType = WireTypeDateAsInt;

template <typename DestinationType>
struct to_value {
static inline void convert(const SourceType & src, DestinationType & dest) {
convert_via_proxy<DataSourceType<DataSourceTypeId::Date>>(src, dest);
}
};
};

template <>
struct from_value<WireTypeDateAsInt>::to_value<DataSourceType<DataSourceTypeId::Date>> {
using DestinationType = DataSourceType<DataSourceTypeId::Date>;

static inline void convert(const SourceType & src, DestinationType & dest) {
std::time_t time = src.value;
time = time * 24 * 60 * 60; // Now it's seconds since epoch.
const auto & tm = *std::localtime(&time);

dest.value.year = 1900 + tm.tm_year;
dest.value.month = 1 + tm.tm_mon;
dest.value.day = tm.tm_mday;
}
};

template <>
struct from_value<WireTypeDateTimeAsInt> {
using SourceType = WireTypeDateTimeAsInt;

template <typename DestinationType>
struct to_value {
static inline void convert(const SourceType & src, DestinationType & dest) {
convert_via_proxy<DataSourceType<DataSourceTypeId::DateTime>>(src, dest);
}
};
};

template <>
struct from_value<WireTypeDateTimeAsInt>::to_value<DataSourceType<DataSourceTypeId::DateTime>> {
using DestinationType = DataSourceType<DataSourceTypeId::DateTime>;

static inline void convert(const SourceType & src, DestinationType & dest) {
std::time_t time = src.value;
const auto & tm = *std::localtime(&time);

dest.value.year = 1900 + tm.tm_year;
dest.value.month = 1 + tm.tm_mon;
dest.value.day = tm.tm_mday;
dest.value.hour = tm.tm_hour;
dest.value.minute = tm.tm_min;
dest.value.second = tm.tm_sec;
dest.value.fraction = 0;
}
};

template <typename DestinationType>
struct to_buffer {
template <typename SourceType>
Expand Down

0 comments on commit d783b44

Please sign in to comment.