Skip to content

Commit 6136364

Browse files
hanneskrlmlr
authored andcommitted
Add ADBC integration with the adbcdrivermanager package
- Merge pull request duckdb/duckdb#8172 from paleolimbot/r-adbcdrivermanager
1 parent a5baf9b commit 6136364

File tree

9 files changed

+107
-2
lines changed

9 files changed

+107
-2
lines changed

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ Suggests:
5959
testthat,
6060
tibble,
6161
vctrs,
62-
withr
62+
withr,
63+
adbcdrivermanager
6364
Encoding: UTF-8
6465
Roxygen: list(markdown = TRUE)
6566
RoxygenNote: 7.2.3

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ S3method(print,duckdb_explain)
77
S3method(print,duckdb_expr)
88
S3method(print,duckdb_relation)
99
export(duckdb)
10+
export(duckdb_adbc)
1011
export(duckdb_fetch_arrow)
1112
export(duckdb_fetch_record_batch)
1213
export(duckdb_get_substrait)

R/Driver.R

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,48 @@ duckdb_shutdown <- function(drv) {
7373
invisible(TRUE)
7474
}
7575

76+
#' @description
77+
#' Return an [adbcdrivermanager::adbc_driver()] for use with Arrow Database
78+
#' Connectivity via the adbcdrivermanager package.
79+
#'
80+
#' @return An object of class "adbc_driver"
81+
#' @rdname duckdb
82+
#' @export
83+
#' @examplesIf requireNamespace("adbcdrivermanager", quietly = TRUE)
84+
#' library(adbcdrivermanager)
85+
#' with_adbc(db <- adbc_database_init(duckdb_adbc()), {
86+
#' as.data.frame(read_adbc(db, "SELECT 1 as one;"))
87+
#' })
88+
duckdb_adbc <- function() {
89+
init_func <- structure(rapi_adbc_init_func(), class = "adbc_driver_init_func")
90+
adbcdrivermanager::adbc_driver(init_func, subclass = "duckdb_driver_adbc")
91+
}
92+
93+
# Registered in zzz.R
94+
adbc_database_init.duckdb_driver_adbc <- function(driver, ...) {
95+
adbcdrivermanager::adbc_database_init_default(
96+
driver,
97+
list(...),
98+
subclass = "duckdb_database_adbc"
99+
)
100+
}
101+
102+
adbc_connection_init.duckdb_database_adbc <- function(database, ...) {
103+
adbcdrivermanager::adbc_connection_init_default(
104+
database,
105+
list(...),
106+
subclass = "duckdb_connection_adbc"
107+
)
108+
}
109+
110+
adbc_statement_init.duckdb_connection_adbc <- function(connection, ...) {
111+
adbcdrivermanager::adbc_statement_init_default(
112+
connection,
113+
list(...),
114+
subclass = "duckdb_statement_adbc"
115+
)
116+
}
117+
76118
is_installed <- function(pkg) {
77119
as.logical(requireNamespace(pkg, quietly = TRUE)) == TRUE
78120
}

R/cpp11.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ rapi_execute <- function(stmt, arrow, integer64) {
196196
.Call(`_duckdb_rapi_execute`, stmt, arrow, integer64)
197197
}
198198

199+
rapi_adbc_init_func <- function() {
200+
.Call(`_duckdb_rapi_adbc_init_func`)
201+
}
202+
199203
rapi_ptr_to_str <- function(extptr) {
200204
.Call(`_duckdb_rapi_ptr_to_str`, extptr)
201205
}

R/zzz.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
s3_register("dbplyr::sql_escape_date", "duckdb_connection")
77
s3_register("dbplyr::sql_escape_datetime", "duckdb_connection")
88
s3_register("dplyr::tbl", "duckdb_connection")
9+
s3_register("adbcdrivermanager::adbc_database_init", "duckdb_driver_adbc")
10+
s3_register("adbcdrivermanager::adbc_connection_init", "duckdb_database_adbc")
11+
s3_register("adbcdrivermanager::adbc_statement_init", "duckdb_connection_adbc")
912

1013
invisible()
1114
}

man/duckdb.Rd

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cpp11.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,13 @@ extern "C" SEXP _duckdb_rapi_execute(SEXP stmt, SEXP arrow, SEXP integer64) {
357357
END_CPP11
358358
}
359359
// utils.cpp
360+
SEXP rapi_adbc_init_func();
361+
extern "C" SEXP _duckdb_rapi_adbc_init_func() {
362+
BEGIN_CPP11
363+
return cpp11::as_sexp(rapi_adbc_init_func());
364+
END_CPP11
365+
}
366+
// utils.cpp
360367
cpp11::r_string rapi_ptr_to_str(SEXP extptr);
361368
extern "C" SEXP _duckdb_rapi_ptr_to_str(SEXP extptr) {
362369
BEGIN_CPP11
@@ -366,6 +373,7 @@ extern "C" SEXP _duckdb_rapi_ptr_to_str(SEXP extptr) {
366373

367374
extern "C" {
368375
static const R_CallMethodDef CallEntries[] = {
376+
{"_duckdb_rapi_adbc_init_func", (DL_FUNC) &_duckdb_rapi_adbc_init_func, 0},
369377
{"_duckdb_rapi_bind", (DL_FUNC) &_duckdb_rapi_bind, 4},
370378
{"_duckdb_rapi_connect", (DL_FUNC) &_duckdb_rapi_connect, 1},
371379
{"_duckdb_rapi_disconnect", (DL_FUNC) &_duckdb_rapi_disconnect, 1},

src/utils.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
#include "duckdb/common/types/timestamp.hpp"
12
#include "rapi.hpp"
23
#include "typesr.hpp"
3-
#include "duckdb/common/types/timestamp.hpp"
44

55
using namespace duckdb;
66

7+
typedef uint8_t AdbcStatusCode;
8+
struct AdbcError;
9+
extern "C" AdbcStatusCode duckdb_adbc_init(int version, void *raw_driver, struct AdbcError *error);
10+
11+
[[cpp11::register]] SEXP rapi_adbc_init_func() {
12+
return R_MakeExternalPtrFn((DL_FUNC)duckdb_adbc_init, R_NilValue, R_NilValue);
13+
}
14+
715
SEXP duckdb::ToUtf8(SEXP string_sexp) {
816
cpp11::function enc2utf8 = RStrings::get().enc2utf8_sym;
917
return enc2utf8(string_sexp);

tests/testthat/test-adbc.R

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
test_that("ADBC driver can create databases, connections, and statements", {
2+
skip_if_not_installed("adbcdrivermanager")
3+
4+
drv <- duckdb_adbc()
5+
expect_s3_class(drv, "duckdb_driver_adbc")
6+
7+
db <- adbcdrivermanager::local_adbc(
8+
adbcdrivermanager::adbc_database_init(duckdb_adbc())
9+
)
10+
expect_s3_class(db, "duckdb_database_adbc")
11+
12+
con <- adbcdrivermanager::local_adbc(
13+
adbcdrivermanager::adbc_connection_init(db)
14+
)
15+
expect_s3_class(con, "duckdb_connection_adbc")
16+
17+
stmt <- adbcdrivermanager::local_adbc(
18+
adbcdrivermanager::adbc_statement_init(con)
19+
)
20+
expect_s3_class(stmt, "duckdb_statement_adbc")
21+
22+
stream <- adbcdrivermanager::read_adbc(con, "SELECT 1 as one;")
23+
expect_identical(as.data.frame(stream), data.frame(one = 1L))
24+
})

0 commit comments

Comments
 (0)