Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 16 additions & 25 deletions cpp/src/arrow/flight/sql/odbc/entry_points.cc
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText,
return arrow::SQLExecDirect(stmt, queryText, textLength);
}

SQLRETURN SQL_API SQLFetch(SQLHSTMT stmt) { return arrow::SQLFetch(stmt); }

SQLRETURN SQL_API SQLGetData(SQLHSTMT stmt, SQLUSMALLINT recordNumber, SQLSMALLINT cType,
SQLPOINTER dataPtr, SQLLEN bufferLength,
SQLLEN* indicatorPtr) {
return arrow::SQLGetData(stmt, recordNumber, cType, dataPtr, bufferLength,
indicatorPtr);
}

SQLRETURN SQL_API SQLBindCol(SQLHSTMT statementHandle, SQLUSMALLINT columnNumber,
SQLSMALLINT targetType, SQLPOINTER targetValuePtr,
SQLLEN bufferLength, SQLLEN* strLen_or_IndPtr) {
Expand Down Expand Up @@ -204,11 +213,6 @@ SQLRETURN SQL_API SQLExecute(SQLHSTMT statementHandle) {
return SQL_ERROR;
}

SQLRETURN SQL_API SQLFetch(SQLHSTMT statementHandle) {
LOG_DEBUG("SQLFetch called with statementHandle: {}", statementHandle);
return SQL_ERROR;
}

SQLRETURN SQL_API SQLForeignKeys(SQLHSTMT statementHandle, SQLWCHAR* pKCatalogName,
SQLSMALLINT pKCatalogNameLength, SQLWCHAR* pKSchemaName,
SQLSMALLINT pKSchemaNameLength, SQLWCHAR* pKTableName,
Expand All @@ -232,27 +236,13 @@ SQLRETURN SQL_API SQLForeignKeys(SQLHSTMT statementHandle, SQLWCHAR* pKCatalogNa
return SQL_ERROR;
}

SQLRETURN SQL_API SQLGetData(SQLHSTMT statementHandle, SQLUSMALLINT col_or_Param_Num,
SQLSMALLINT targetType, SQLPOINTER targetValuePtr,
SQLLEN bufferLength, SQLLEN* strLen_or_IndPtr) {
LOG_DEBUG(
"SQLGetData called with statementHandle: {}, col_or_Param_Num: {}, targetType: {}, "
"targetValuePtr: {}, bufferLength: {}, strLen_or_IndPtr: {}",
statementHandle, col_or_Param_Num, targetType, targetValuePtr, bufferLength,
fmt::ptr(strLen_or_IndPtr));
return SQL_ERROR;
}

SQLRETURN SQL_API SQLGetTypeInfo(SQLHSTMT statementHandle, SQLSMALLINT dataType) {
LOG_DEBUG("SQLGetTypeInfoW called with statementHandle: {} dataType: {}",
statementHandle, dataType);
return SQL_ERROR;
}

SQLRETURN SQL_API SQLMoreResults(SQLHSTMT statementHandle) {
LOG_DEBUG("SQLMoreResults called with statementHandle: {}", statementHandle);
return SQL_ERROR;
}
SQLRETURN SQL_API SQLMoreResults(SQLHSTMT stmt) { return arrow::SQLMoreResults(stmt); }

SQLRETURN SQL_API SQLNativeSql(SQLHDBC connectionHandle, SQLWCHAR* inStatementText,
SQLINTEGER inStatementTextLength,
Expand All @@ -267,11 +257,12 @@ SQLRETURN SQL_API SQLNativeSql(SQLHDBC connectionHandle, SQLWCHAR* inStatementTe
return SQL_ERROR;
}

SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT statementHandle,
SQLSMALLINT* columnCountPtr) {
LOG_DEBUG("SQLNumResultCols called with statementHandle: {}, columnCountPtr: {}",
statementHandle, fmt::ptr(columnCountPtr));
return SQL_ERROR;
SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) {
return arrow::SQLNumResultCols(stmt, columnCountPtr);
}

SQLRETURN SQL_API SQLRowCount(SQLHSTMT stmt, SQLLEN* rowCountPtr) {
return arrow::SQLRowCount(stmt, rowCountPtr);
}

SQLRETURN SQL_API SQLPrepare(SQLHSTMT statementHandle, SQLWCHAR* statementText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,24 @@ using arrow::NumericArray;

using odbcabstraction::DATE_STRUCT;
using odbcabstraction::OdbcVersion;
using odbcabstraction::tagDATE_STRUCT;

using arrow::ArrayFromVector;
using odbcabstraction::GetTimeForSecondsSinceEpoch;

TEST(DateArrayAccessor, Test_Date32Array_CDataType_DATE) {
std::vector<int32_t> values = {7589, 12320, 18980, 19095};
std::vector<int32_t> values = {7589, 12320, 18980, 19095, -1, 0};
std::vector<DATE_STRUCT> expected = {
{1990, 10, 12}, {2003, 9, 25}, {2021, 12, 19},
{2022, 4, 13}, {1969, 12, 31}, {1970, 1, 1},
};

std::shared_ptr<Array> array;
ArrayFromVector<Date32Type, int32_t>(values, &array);

DateArrayFlightSqlAccessor<odbcabstraction::CDataType_DATE, Date32Array> accessor(
dynamic_cast<NumericArray<Date32Type>*>(array.get()));

std::vector<tagDATE_STRUCT> buffer(values.size());
std::vector<DATE_STRUCT> buffer(values.size());
std::vector<ssize_t> strlen_buffer(values.size());

ColumnBinding binding(odbcabstraction::CDataType_DATE, 0, 0, buffer.data(), 0,
Expand All @@ -60,27 +63,40 @@ TEST(DateArrayAccessor, Test_Date32Array_CDataType_DATE) {

for (size_t i = 0; i < values.size(); ++i) {
ASSERT_EQ(sizeof(DATE_STRUCT), strlen_buffer[i]);
tm date{};

int64_t converted_time = values[i] * 86400;
GetTimeForSecondsSinceEpoch(date, converted_time);
ASSERT_EQ((date.tm_year + 1900), buffer[i].year);
ASSERT_EQ(date.tm_mon + 1, buffer[i].month);
ASSERT_EQ(date.tm_mday, buffer[i].day);
ASSERT_EQ(expected[i].year, buffer[i].year);
ASSERT_EQ(expected[i].month, buffer[i].month);
ASSERT_EQ(expected[i].day, buffer[i].day);
}
}

TEST(DateArrayAccessor, Test_Date64Array_CDataType_DATE) {
std::vector<int64_t> values = {86400000, 172800000, 259200000, 1649793238110,
345600000, 432000000, 518400000};
std::vector<int64_t> values = {86400000, 172800000, 259200000, 1649793238110,
0, 345600000, 432000000, 518400000,
-86400000, -17987443200000};
std::vector<DATE_STRUCT> expected = {

/* year(16), month(u16), day(u16) */
{1970, 1, 2},
{1970, 1, 3},
{1970, 1, 4},
{2022, 4, 12},
{1970, 1, 1},
{1970, 1, 5},
{1970, 1, 6},
{1970, 1, 7},
{1969, 12, 31},
// This is the documented lower limit of supported Gregorian dates for boost
{1400, 1, 1},
};

std::shared_ptr<Array> array;
ArrayFromVector<Date64Type, int64_t>(values, &array);

DateArrayFlightSqlAccessor<odbcabstraction::CDataType_DATE, Date64Array> accessor(
dynamic_cast<NumericArray<Date64Type>*>(array.get()));

std::vector<tagDATE_STRUCT> buffer(values.size());
std::vector<DATE_STRUCT> buffer(values.size());
std::vector<ssize_t> strlen_buffer(values.size());

ColumnBinding binding(odbcabstraction::CDataType_DATE, 0, 0, buffer.data(), 0,
Expand All @@ -96,11 +112,9 @@ TEST(DateArrayAccessor, Test_Date64Array_CDataType_DATE) {
ASSERT_EQ(sizeof(DATE_STRUCT), strlen_buffer[i]);
tm date{};

int64_t converted_time = values[i] / 1000;
GetTimeForSecondsSinceEpoch(date, converted_time);
ASSERT_EQ((date.tm_year + 1900), buffer[i].year);
ASSERT_EQ(date.tm_mon + 1, buffer[i].month);
ASSERT_EQ(date.tm_mday, buffer[i].day);
ASSERT_EQ(expected[i].year, buffer[i].year);
ASSERT_EQ(expected[i].month, buffer[i].month);
ASSERT_EQ(expected[i].day, buffer[i].day);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
#include "arrow/flight/sql/odbc/flight_sql/accessors/timestamp_array_accessor.h"
#include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/calendar_utils.h"

#include <cmath>
#include <limits>

using arrow::TimeUnit;

namespace {
int64_t GetConversionToSecondsDivisor(TimeUnit::type unit) {
inline int64_t GetConversionToSecondsDivisor(TimeUnit::type unit) {
int64_t divisor = 1;
switch (unit) {
case TimeUnit::SECOND:
Expand All @@ -44,25 +47,26 @@ int64_t GetConversionToSecondsDivisor(TimeUnit::type unit) {
return divisor;
}

uint32_t CalculateFraction(TimeUnit::type unit, uint64_t units_since_epoch) {
uint32_t CalculateFraction(TimeUnit::type unit, int64_t units_since_epoch) {
// Convert the given remainder and time unit to nanoseconds
// since the fraction field on TIMESTAMP_STRUCT is in nanoseconds.
switch (unit) {
case TimeUnit::SECOND:
return 0;
case TimeUnit::MILLI:
// 1000000 nanoseconds = 1 millisecond.
return (units_since_epoch % driver::odbcabstraction::MILLI_TO_SECONDS_DIVISOR) *
1000000;
case TimeUnit::MICRO:
// 1000 nanoseconds = 1 microsecond.
return (units_since_epoch % driver::odbcabstraction::MICRO_TO_SECONDS_DIVISOR) *
1000;
case TimeUnit::NANO:
// 1000 nanoseconds = 1 microsecond.
return (units_since_epoch % driver::odbcabstraction::NANO_TO_SECONDS_DIVISOR);
}
return 0;
if (unit == TimeUnit::SECOND) return 0;

const int64_t divisor = GetConversionToSecondsDivisor(unit);
const int64_t nano_divisor = GetConversionToSecondsDivisor(TimeUnit::NANO);

if (units_since_epoch < 0)
if (units_since_epoch <=
(std::numeric_limits<decltype(units_since_epoch)>::min() + divisor))
// Prevent trying to derive and add a value larger than INT64_MAX (i.e. the time
// value at the start of the second which is used to shift the value positive before
// the modulo operation)) in next statement.
units_since_epoch += divisor;
// See below regarding floor division; here we want ceiling division.
// FIXME this goes poorly (trying to use a value > INT64_MAX when units_since_epoch is
// less than the smallest multiple of divisor greater than INT64_MIN.
units_since_epoch += divisor * std::abs((units_since_epoch - (divisor - 1)) / divisor);
return static_cast<uint32_t>((units_since_epoch % divisor) * (nano_divisor / divisor));
}
} // namespace

Expand All @@ -84,11 +88,25 @@ RowStatus TimestampArrayFlightSqlAccessor<TARGET_TYPE, UNIT>::MoveSingleCell_imp
ColumnBinding* binding, int64_t arrow_row, int64_t cell_counter,
int64_t& value_offset, bool update_value_offset,
odbcabstraction::Diagnostics& diagnostics) {
// Times less than the minimum integer number of seconds that can be represented
// for each time unit will not convert correctly. This is mostly interesting for
// nanoseconds as timestamps in other units are outside of the accepted range of
// Gregorian dates.
auto* buffer = static_cast<TIMESTAMP_STRUCT*>(binding->buffer);

int64_t value = this->GetArray()->Value(arrow_row);
const auto divisor = GetConversionToSecondsDivisor(UNIT);
const auto converted_result_seconds = value / divisor;
const auto converted_result_seconds =
// We want floor division here; C++ will round towards zero
(value < 0)
// Floor division: Shift all "fractional" (not a multiple of divisor) values so
// they round towards zero (and to the same value) along with the "floor" less
// than them, then add 1 to get back to the floor. Althernative we could shift
// negatively by (divisor - 1) but this breaks near INT64_MIN causing
// underflow..
? ((value + 1) / divisor) - 1
// Towards zero is already floor
: value / divisor;
tm timestamp = {0};

GetTimeForSecondsSinceEpoch(timestamp, converted_result_seconds);
Expand Down
Loading
Loading