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/common): Add minimal C++ driver framework #1539

Merged
merged 7 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions c/driver/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ if(ADBC_BUILD_TESTS)
driver-common
SOURCES
utils_test.cc
driver_test.cc
EXTRA_LINK_LIBS
adbc_driver_common
nanoarrow)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

namespace adbc {

namespace r {
namespace common {

class Error {
public:
Expand All @@ -63,6 +63,10 @@ class Error {
}

void ToAdbc(AdbcError* adbc_error, AdbcDriver* driver = nullptr) {
if (adbc_error == nullptr) {
return;
}

if (adbc_error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA) {
auto error_owned_by_adbc_error =
new Error(std::move(message_), std::move(details_));
Expand Down Expand Up @@ -95,7 +99,7 @@ class Error {
AdbcErrorDetail CDetail(int index) const {
const auto& detail = details_[index];
return {detail.first.c_str(), reinterpret_cast<const uint8_t*>(detail.second.data()),
detail.second.size()};
detail.second.size() + 1};
}

static void CRelease(AdbcError* error) {
Expand Down Expand Up @@ -308,15 +312,15 @@ class ObjectBase {
std::stringstream msg_builder;
msg_builder << "Option not found for key '" << key << "'";
Error cpperror(msg_builder.str());
cpperror.AddDetail("adbc.r.option_key", key);
cpperror.AddDetail("adbc.driver_base.option_key", key);
cpperror.ToAdbc(error, driver());
}

void InitErrorWrongType(const char* key, AdbcError* error) const {
std::stringstream msg_builder;
msg_builder << "Wrong type requested for option key '" << key << "'";
Error cpperror(msg_builder.str());
cpperror.AddDetail("adbc.r.option_key", key);
cpperror.AddDetail("adbc.driver_base.option_key", key);
cpperror.ToAdbc(error, driver());
}
};
Expand Down Expand Up @@ -761,6 +765,6 @@ class Driver {
}
};

} // namespace r
} // namespace common

} // namespace adbc
271 changes: 271 additions & 0 deletions c/driver/common/driver_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// 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 <gtest/gtest.h>
#include <cstring>

#include <adbc.h>
#include "driver_base.h"

// Self-contained version of the Handle
static inline void clean_up(AdbcDriver* ptr) { ptr->release(ptr, nullptr); }

static inline void clean_up(AdbcDatabase* ptr) {
ptr->private_driver->DatabaseRelease(ptr, nullptr);
}

static inline void clean_up(AdbcConnection* ptr) {
ptr->private_driver->ConnectionRelease(ptr, nullptr);
}

static inline void clean_up(AdbcStatement* ptr) {
ptr->private_driver->StatementRelease(ptr, nullptr);
}

static inline void clean_up(AdbcError* ptr) {
if (ptr->release != nullptr) {
ptr->release(ptr);
}
}

template <typename T>
class Handle {
public:
explicit Handle(T* value) : value_(value) {}

~Handle() { clean_up(value_); }

private:
T* value_;
};

class VoidDatabase : public adbc::common::DatabaseObjectBase {};

class VoidConnection : public adbc::common::ConnectionObjectBase {};

class VoidStatement : public adbc::common::StatementObjectBase {};

using VoidDriver = adbc::common::Driver<VoidDatabase, VoidConnection, VoidStatement>;

AdbcStatusCode VoidDriverInitFunc(int version, void* raw_driver, AdbcError* error) {
return VoidDriver::Init(version, raw_driver, error);
}

TEST(TestDriverBase, TestVoidDriverOptions) {
// Test the get/set option implementation in the base driver
struct AdbcDriver driver;
memset(&driver, 0, sizeof(driver));
ASSERT_EQ(VoidDriverInitFunc(ADBC_VERSION_1_1_0, &driver, nullptr), ADBC_STATUS_OK);
Handle<AdbcDriver> driver_handle(&driver);

struct AdbcDatabase database;
memset(&database, 0, sizeof(database));
ASSERT_EQ(driver.DatabaseNew(&database, nullptr), ADBC_STATUS_OK);
database.private_driver = &driver;
Handle<AdbcDatabase> database_handle(&database);
ASSERT_EQ(driver.DatabaseInit(&database, nullptr), ADBC_STATUS_OK);

std::vector<char> opt_string;
std::vector<uint8_t> opt_bytes;
size_t opt_size = 0;
int64_t opt_int = 0;
double opt_double = 0;

// Check return codes without an error pointer for non-existent keys
ASSERT_EQ(driver.DatabaseGetOption(&database, "key_that_does_not_exist", nullptr,
&opt_size, nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(driver.DatabaseGetOptionBytes(&database, "key_that_does_not_exist", nullptr,
&opt_size, nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(driver.DatabaseGetOptionInt(&database, "key_that_does_not_exist", &opt_int,
nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(driver.DatabaseGetOptionDouble(&database, "key_that_does_not_exist",
&opt_double, nullptr),
ADBC_STATUS_NOT_FOUND);

// Check set/get for string
ASSERT_EQ(driver.DatabaseSetOption(&database, "key_string", "value_string", nullptr),
ADBC_STATUS_OK);
opt_size = 0;
ASSERT_EQ(
driver.DatabaseGetOption(&database, "key_string", nullptr, &opt_size, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(opt_size, strlen("value_string") + 1);
opt_string.resize(opt_size);
ASSERT_EQ(driver.DatabaseGetOption(&database, "key_string", opt_string.data(),
&opt_size, nullptr),
ADBC_STATUS_OK);

// Check set/get for bytes
const uint8_t test_bytes[] = {0x01, 0x02, 0x03};
ASSERT_EQ(driver.DatabaseSetOptionBytes(&database, "key_bytes", test_bytes,
sizeof(test_bytes), nullptr),
ADBC_STATUS_OK);
opt_size = 0;
ASSERT_EQ(
driver.DatabaseGetOptionBytes(&database, "key_bytes", nullptr, &opt_size, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(opt_size, sizeof(test_bytes));
opt_bytes.resize(opt_size);
ASSERT_EQ(driver.DatabaseGetOptionBytes(&database, "key_bytes", opt_bytes.data(),
&opt_size, nullptr),
ADBC_STATUS_OK);

// Check set/get for int
ASSERT_EQ(driver.DatabaseSetOptionInt(&database, "key_int", 1234, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(driver.DatabaseGetOptionInt(&database, "key_int", &opt_int, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(opt_int, 1234);

// Check set/get for double
ASSERT_EQ(driver.DatabaseSetOptionDouble(&database, "key_double", 1234.5, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(driver.DatabaseGetOptionDouble(&database, "key_double", &opt_double, nullptr),
ADBC_STATUS_OK);
ASSERT_EQ(opt_double, 1234.5);

// Check error code for getting a key of an incorrect type
opt_size = 0;
ASSERT_EQ(driver.DatabaseGetOption(&database, "key_bytes", nullptr, &opt_size, nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(
driver.DatabaseGetOptionBytes(&database, "key_string", nullptr, &opt_size, nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(driver.DatabaseGetOptionInt(&database, "key_bytes", &opt_int, nullptr),
ADBC_STATUS_NOT_FOUND);
ASSERT_EQ(driver.DatabaseGetOptionDouble(&database, "key_bytes", &opt_double, nullptr),
ADBC_STATUS_NOT_FOUND);
}

TEST(TestDriverBase, TestVoidDriverError) {
// Test the extended error detail implementation in the base driver
struct AdbcDriver driver;
memset(&driver, 0, sizeof(driver));
ASSERT_EQ(VoidDriverInitFunc(ADBC_VERSION_1_1_0, &driver, nullptr), ADBC_STATUS_OK);
Handle<AdbcDriver> driver_handle(&driver);

struct AdbcDatabase database;
memset(&database, 0, sizeof(database));
ASSERT_EQ(driver.DatabaseNew(&database, nullptr), ADBC_STATUS_OK);
database.private_driver = &driver;
Handle<AdbcDatabase> database_handle(&database);
ASSERT_EQ(driver.DatabaseInit(&database, nullptr), ADBC_STATUS_OK);

struct AdbcError error;
memset(&error, 0, sizeof(error));
Handle<AdbcError> error_handle(&error);
size_t opt_size = 0;

// With zero-initialized error, should populate message but not details
ASSERT_EQ(driver.DatabaseGetOption(&database, "key_does_not_exist", nullptr, &opt_size,
&error),
ADBC_STATUS_NOT_FOUND);
EXPECT_EQ(error.vendor_code, 0);
EXPECT_STREQ(error.message, "Option not found for key 'key_does_not_exist'");
EXPECT_EQ(error.private_data, nullptr);
EXPECT_EQ(error.private_driver, nullptr);

// Release callback implementation should reset callback
error.release(&error);
ASSERT_EQ(error.release, nullptr);

// With the vendor code pre-set, should populate a version with details
memset(&error, 0, sizeof(error));
error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA;

ASSERT_EQ(driver.DatabaseGetOption(&database, "key_does_not_exist", nullptr, &opt_size,
&error),
ADBC_STATUS_NOT_FOUND);
EXPECT_NE(error.private_data, nullptr);
EXPECT_EQ(error.private_driver, &driver);

ASSERT_EQ(error.private_driver->ErrorGetDetailCount(&error), 1);

struct AdbcErrorDetail detail = error.private_driver->ErrorGetDetail(&error, 0);
ASSERT_STREQ(detail.key, "adbc.driver_base.option_key");
ASSERT_EQ(detail.value_length, strlen("key_does_not_exist") + 1);
ASSERT_STREQ(reinterpret_cast<const char*>(detail.value), "key_does_not_exist");
}

TEST(TestDriverBase, TestVoidDriverMethods) {
struct AdbcDriver driver;
memset(&driver, 0, sizeof(driver));
ASSERT_EQ(VoidDriverInitFunc(ADBC_VERSION_1_1_0, &driver, nullptr), ADBC_STATUS_OK);
Handle<AdbcDriver> driver_handle(&driver);

// Database methods are only option related
struct AdbcDatabase database;
memset(&database, 0, sizeof(database));
ASSERT_EQ(driver.DatabaseNew(&database, nullptr), ADBC_STATUS_OK);
database.private_driver = &driver;
Handle<AdbcDatabase> database_handle(&database);
ASSERT_EQ(driver.DatabaseInit(&database, nullptr), ADBC_STATUS_OK);

// Test connection methods
struct AdbcConnection connection;
memset(&connection, 0, sizeof(connection));
ASSERT_EQ(driver.ConnectionNew(&connection, nullptr), ADBC_STATUS_OK);
connection.private_driver = &driver;
Handle<AdbcConnection> connection_handle(&connection);
ASSERT_EQ(driver.ConnectionInit(&connection, &database, nullptr), ADBC_STATUS_OK);

EXPECT_EQ(driver.ConnectionCommit(&connection, nullptr), ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetInfo(&connection, nullptr, 0, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetObjects(&connection, 0, nullptr, nullptr, 0, nullptr,
nullptr, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetTableSchema(&connection, nullptr, nullptr, nullptr,
nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetTableTypes(&connection, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionReadPartition(&connection, nullptr, 0, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionRollback(&connection, nullptr), ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionCancel(&connection, nullptr), ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetStatistics(&connection, nullptr, nullptr, nullptr, 0,
nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.ConnectionGetStatisticNames(&connection, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);

// Test statement methods
struct AdbcStatement statement;
memset(&statement, 0, sizeof(statement));
ASSERT_EQ(driver.StatementNew(&connection, &statement, nullptr), ADBC_STATUS_OK);
statement.private_driver = &driver;
Handle<AdbcStatement> statement_handle(&statement);

EXPECT_EQ(driver.StatementExecuteQuery(&statement, nullptr, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementExecuteSchema(&statement, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementPrepare(&statement, nullptr), ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementSetSqlQuery(&statement, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementSetSubstraitPlan(&statement, nullptr, 0, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementBind(&statement, nullptr, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementBindStream(&statement, nullptr, nullptr),
ADBC_STATUS_NOT_IMPLEMENTED);
EXPECT_EQ(driver.StatementCancel(&statement, nullptr), ADBC_STATUS_NOT_IMPLEMENTED);
}
3 changes: 2 additions & 1 deletion r/adbcdrivermanager/bootstrap.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
files_to_vendor <- c(
"../../adbc.h",
"../../c/driver_manager/adbc_driver_manager.h",
"../../c/driver_manager/adbc_driver_manager.cc"
"../../c/driver_manager/adbc_driver_manager.cc",
"../../c/driver/common/driver_base.h"
)

if (all(file.exists(files_to_vendor))) {
Expand Down
2 changes: 2 additions & 0 deletions r/adbcdrivermanager/configure
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ curl -L https://github.com/apache/arrow-adbc/raw/main/c/driver_manager/adbc_driv
--output src/adbc_driver_manager.h --silent
curl -L https://github.com/apache/arrow-adbc/raw/main/c/driver_manager/adbc_driver_manager.cc \
--output src/adbc_driver_manager.cc --silent
curl -L https://github.com/apache/arrow-adbc/raw/main/c/driver/common/driver_base.h \
--output src/driver_base.h --silent

if [ -f "src/adbc.h" ] && [ -f "src/adbc_driver_manager.h" ] && [ -f "src/adbc_driver_manager.cc" ]; then
echo "Fetched ADBC from https://github.com/apache/arrow-adbc"
Expand Down
1 change: 1 addition & 0 deletions r/adbcdrivermanager/src/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
adbc.h
adbc_driver_manager.h
adbc_driver_manager.cc
driver_base.h
Loading
Loading