Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(c/driver/postgresql): Duration support #907

Merged
merged 16 commits into from
Sep 7, 2023
88 changes: 71 additions & 17 deletions c/driver/postgresql/postgresql_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class PostgresQuirks : public adbc_validation::DriverQuirks {
switch (ingest_type) {
case NANOARROW_TYPE_INT8:
return NANOARROW_TYPE_INT16;
case NANOARROW_TYPE_DURATION:
return NANOARROW_TYPE_INTERVAL_MONTH_DAY_NANO;
default:
return ingest_type;
}
Expand Down Expand Up @@ -796,26 +798,78 @@ class PostgresStatementTest : public ::testing::Test,
}

protected:
void ValidateIngestedTimestampData(struct ArrowArrayView* values,
enum ArrowTimeUnit unit,
const char* timezone) override {
std::vector<std::optional<int64_t>> expected;
switch (unit) {
case (NANOARROW_TIME_UNIT_SECOND):
expected.insert(expected.end(), {std::nullopt, -42000000, 0, 42000000});
break;
case (NANOARROW_TIME_UNIT_MILLI):
expected.insert(expected.end(), {std::nullopt, -42000, 0, 42000});
break;
case (NANOARROW_TIME_UNIT_MICRO):
expected.insert(expected.end(), {std::nullopt, -42, 0, 42});
void ValidateIngestedTemporalData(struct ArrowArrayView* values, ArrowType type,
enum ArrowTimeUnit unit,
const char* timezone) override {
switch (type) {
case NANOARROW_TYPE_TIMESTAMP: {
std::vector<std::optional<int64_t>> expected;
switch (unit) {
case (NANOARROW_TIME_UNIT_SECOND):
expected.insert(expected.end(), {std::nullopt, -42000000, 0, 42000000});
break;
case (NANOARROW_TIME_UNIT_MILLI):
expected.insert(expected.end(), {std::nullopt, -42000, 0, 42000});
break;
case (NANOARROW_TIME_UNIT_MICRO):
expected.insert(expected.end(), {std::nullopt, -42, 0, 42});
break;
case (NANOARROW_TIME_UNIT_NANO):
expected.insert(expected.end(), {std::nullopt, 0, 0, 0});
break;
}
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<std::int64_t>(values, expected));
break;
case (NANOARROW_TIME_UNIT_NANO):
expected.insert(expected.end(), {std::nullopt, 0, 0, 0});
}
case NANOARROW_TYPE_DURATION: {
struct ArrowInterval neg_interval;
struct ArrowInterval zero_interval;
struct ArrowInterval pos_interval;

ArrowIntervalInit(&neg_interval, type);
ArrowIntervalInit(&zero_interval, type);
ArrowIntervalInit(&pos_interval, type);

neg_interval.months = 0;
neg_interval.days = 0;
zero_interval.months = 0;
zero_interval.days = 0;
pos_interval.months = 0;
pos_interval.days = 0;

switch (unit) {
case (NANOARROW_TIME_UNIT_SECOND):
neg_interval.ns = -42000000000;
zero_interval.ns = 0;
pos_interval.ns = 42000000000;
break;
case (NANOARROW_TIME_UNIT_MILLI):
neg_interval.ns = -42000000;
zero_interval.ns = 0;
pos_interval.ns = 42000000;
break;
case (NANOARROW_TIME_UNIT_MICRO):
neg_interval.ns = -42000;
zero_interval.ns = 0;
pos_interval.ns = 42000;
break;
case (NANOARROW_TIME_UNIT_NANO):
// lower than us precision is lost
neg_interval.ns = 0;
zero_interval.ns = 0;
pos_interval.ns = 0;
break;
}
const std::vector<std::optional<ArrowInterval*>> expected = {
std::nullopt, &neg_interval, &zero_interval, &pos_interval};
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<ArrowInterval*>(values, expected));
break;
}
default:
FAIL() << "ValidateIngestedTemporalData not implemented for type " << type;
}
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<std::int64_t>(values, expected));
}

PostgresQuirks quirks_;
Expand Down
17 changes: 15 additions & 2 deletions c/driver/postgresql/statement.cc
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ struct BindStream {
type_id = PostgresTypeId::kTimestamp;
param_lengths[i] = 8;
break;
case ArrowType::NANOARROW_TYPE_DURATION:
case ArrowType::NANOARROW_TYPE_INTERVAL_MONTH_DAY_NANO:
type_id = PostgresTypeId::kInterval;
param_lengths[i] = 16;
Expand Down Expand Up @@ -417,6 +418,7 @@ struct BindStream {
std::memcpy(param_values[col], &value, sizeof(int32_t));
break;
}
case ArrowType::NANOARROW_TYPE_DURATION:
case ArrowType::NANOARROW_TYPE_TIMESTAMP: {
int64_t val = array_view->children[col]->buffer_views[1].data.as_int64[row];

Expand Down Expand Up @@ -448,8 +450,18 @@ struct BindStream {
return ADBC_STATUS_INVALID_ARGUMENT;
}

const uint64_t value = ToNetworkInt64(val - kPostgresTimestampEpoch);
std::memcpy(param_values[col], &value, sizeof(int64_t));
if (bind_schema_fields[col].type == ArrowType::NANOARROW_TYPE_TIMESTAMP) {
const uint64_t value = ToNetworkInt64(val - kPostgresTimestampEpoch);
std::memcpy(param_values[col], &value, sizeof(int64_t));
} else if (bind_schema_fields[col].type ==
ArrowType::NANOARROW_TYPE_DURATION) {
// postgres stores an interval as a 64 bit offset in microsecond
// resolution alongside a 32 bit day and 32 bit month
// for now we just send 0 for the day / month values
const uint64_t value = ToNetworkInt64(val);
std::memcpy(param_values[col], &value, sizeof(int64_t));
std::memset(param_values[col] + sizeof(int64_t), 0, sizeof(int64_t));
}
break;
}
case ArrowType::NANOARROW_TYPE_INTERVAL_MONTH_DAY_NANO: {
Expand Down Expand Up @@ -878,6 +890,7 @@ AdbcStatusCode PostgresStatement::CreateBulkTable(
create += " TIMESTAMP";
}
break;
case ArrowType::NANOARROW_TYPE_DURATION:
case ArrowType::NANOARROW_TYPE_INTERVAL_MONTH_DAY_NANO:
create += " INTERVAL";
break;
Expand Down
44 changes: 26 additions & 18 deletions c/driver/snowflake/snowflake_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,28 +184,36 @@ class SnowflakeStatementTest : public ::testing::Test,
}

void TestSqlIngestInterval() { GTEST_SKIP(); }
void TestSqlIngestDuration() { GTEST_SKIP(); }

protected:
void ValidateIngestedTimestampData(struct ArrowArrayView* values,
enum ArrowTimeUnit unit,
const char* timezone) override {
std::vector<std::optional<int64_t>> expected;
switch (unit) {
case NANOARROW_TIME_UNIT_SECOND:
expected = {std::nullopt, -42, 0, 42};
break;
case NANOARROW_TIME_UNIT_MILLI:
expected = {std::nullopt, -42000, 0, 42000};
break;
case NANOARROW_TIME_UNIT_MICRO:
expected = {std::nullopt, -42, 0, 42};
break;
case NANOARROW_TIME_UNIT_NANO:
expected = {std::nullopt, -42, 0, 42};
void ValidateIngestedTemporalData(struct ArrowArrayView* values, ArrowType type,
enum ArrowTimeUnit unit,
const char* timezone) override {
switch (type) {
case NANOARROW_TYPE_TIMESTAMP: {
std::vector<std::optional<int64_t>> expected;
switch (unit) {
case NANOARROW_TIME_UNIT_SECOND:
expected = {std::nullopt, -42, 0, 42};
break;
case NANOARROW_TIME_UNIT_MILLI:
expected = {std::nullopt, -42000, 0, 42000};
break;
case NANOARROW_TIME_UNIT_MICRO:
expected = {std::nullopt, -42, 0, 42};
break;
case NANOARROW_TIME_UNIT_NANO:
expected = {std::nullopt, -42, 0, 42};
break;
}
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<std::int64_t>(values, expected));
break;
}
default:
FAIL() << "ValidateIngestedTemporalData not implemented for type " << type;
}
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<std::int64_t>(values, expected));
}

SnowflakeQuirks quirks_;
Expand Down
61 changes: 37 additions & 24 deletions c/driver/sqlite/sqlite_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,37 +216,50 @@ class SqliteStatementTest : public ::testing::Test,
}

void TestSqlIngestBinary() { GTEST_SKIP() << "Cannot ingest BINARY (not implemented)"; }
void TestSqlIngestDuration() {
GTEST_SKIP() << "Cannot ingest DURATION (not implemented)";
}
void TestSqlIngestInterval() {
GTEST_SKIP() << "Cannot ingest Interval (not implemented)";
}

protected:
void ValidateIngestedTimestampData(struct ArrowArrayView* values,
enum ArrowTimeUnit unit,
const char* timezone) override {
std::vector<std::optional<std::string>> expected;
switch (unit) {
case (NANOARROW_TIME_UNIT_SECOND):
expected.insert(expected.end(), {std::nullopt, "1969-12-31T23:59:18",
"1970-01-01T00:00:00", "1970-01-01T00:00:42"});
break;
case (NANOARROW_TIME_UNIT_MILLI):
expected.insert(expected.end(),
{std::nullopt, "1969-12-31T23:59:59.958",
"1970-01-01T00:00:00.000", "1970-01-01T00:00:00.042"});
break;
case (NANOARROW_TIME_UNIT_MICRO):
expected.insert(expected.end(),
{std::nullopt, "1969-12-31T23:59:59.999958",
"1970-01-01T00:00:00.000000", "1970-01-01T00:00:00.000042"});
break;
case (NANOARROW_TIME_UNIT_NANO):
expected.insert(expected.end(), {std::nullopt, "1969-12-31T23:59:59.999999958",
"1970-01-01T00:00:00.000000000",
"1970-01-01T00:00:00.000000042"});
void ValidateIngestedTemporalData(struct ArrowArrayView* values, ArrowType type,
enum ArrowTimeUnit unit,
const char* timezone) override {
switch (type) {
case NANOARROW_TYPE_TIMESTAMP: {
std::vector<std::optional<std::string>> expected;
switch (unit) {
case (NANOARROW_TIME_UNIT_SECOND):
expected.insert(expected.end(),
{std::nullopt, "1969-12-31T23:59:18", "1970-01-01T00:00:00",
"1970-01-01T00:00:42"});
break;
case (NANOARROW_TIME_UNIT_MILLI):
expected.insert(expected.end(),
{std::nullopt, "1969-12-31T23:59:59.958",
"1970-01-01T00:00:00.000", "1970-01-01T00:00:00.042"});
break;
case (NANOARROW_TIME_UNIT_MICRO):
expected.insert(expected.end(),
{std::nullopt, "1969-12-31T23:59:59.999958",
"1970-01-01T00:00:00.000000", "1970-01-01T00:00:00.000042"});
break;
case (NANOARROW_TIME_UNIT_NANO):
expected.insert(
expected.end(),
{std::nullopt, "1969-12-31T23:59:59.999999958",
"1970-01-01T00:00:00.000000000", "1970-01-01T00:00:00.000000042"});
break;
}
ASSERT_NO_FATAL_FAILURE(
adbc_validation::CompareArray<std::string>(values, expected));
break;
}
default:
FAIL() << "ValidateIngestedTemporalData not implemented for type " << type;
}
ASSERT_NO_FATAL_FAILURE(adbc_validation::CompareArray<std::string>(values, expected));
}

SqliteQuirks quirks_;
Expand Down
4 changes: 3 additions & 1 deletion c/driver_manager/adbc_driver_manager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ class SqliteStatementTest : public ::testing::Test,
void TestSqlIngestTimestampTz() {
GTEST_SKIP() << "Cannot ingest TIMESTAMP WITH TIMEZONE (not implemented)";
}
void TestSqlIngestDuration() {
GTEST_SKIP() << "Cannot ingest DURATION (not implemented)";
}
void TestSqlIngestInterval() {
GTEST_SKIP() << "Cannot ingest Interval (not implemented)";
}
Expand Down Expand Up @@ -315,5 +318,4 @@ TEST(AdbcDriverManagerInternal, AdbcDriverManagerDefaultEntrypoint) {
EXPECT_EQ("AdbcProprietaryEngineInit", ::AdbcDriverManagerDefaultEntrypoint(driver));
}
}

} // namespace adbc
Loading
Loading