Skip to content

Commit a3ecced

Browse files
authored
feat: New duckdb.materialize_callback option, supersedes get_last_rel() (#589)
1 parent b580b09 commit a3ecced

File tree

9 files changed

+55
-47
lines changed

9 files changed

+55
-47
lines changed

R/cpp11.R

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,6 @@ rapi_rel_from_table_function <- function(con, function_name, positional_paramete
172172
.Call(`_duckdb_rapi_rel_from_table_function`, con, function_name, positional_parameters_sexps, named_parameters_sexps)
173173
}
174174

175-
rapi_get_last_rel <- function() {
176-
.Call(`_duckdb_rapi_get_last_rel`)
177-
}
178-
179175
# allow_materialization = TRUE: compatibility with duckplyr <= 0.4.1
180176
rapi_rel_to_altrep <- function(rel, allow_materialization = TRUE) {
181177
.Call(`_duckdb_rapi_rel_to_altrep`, rel, allow_materialization)

R/relational.R

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ rel_from_df <- function(con, df, experimental=FALSE) {
8686

8787
#' @export
8888
print.duckdb_relation <- function(x, ...) {
89-
message("DuckDB Relation: \n", rapi_rel_tostring(x))
89+
message("DuckDB Relation: \n", rel_tostring(x))
9090
}
9191

9292
#' @export
@@ -503,7 +503,3 @@ rel_names <- function(rel) {
503503
load_rfuns <- function() {
504504
rethrow_rapi_load_rfuns()
505505
}
506-
507-
get_last_rel <- function() {
508-
rethrow_rapi_get_last_rel()
509-
}

R/rethrow-gen.R

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -387,15 +387,6 @@ rethrow_rapi_rel_from_table_function <- function(con, function_name, positional_
387387
)
388388
}
389389

390-
rethrow_rapi_get_last_rel <- function(call = parent.frame(2)) {
391-
rlang::try_fetch(
392-
rapi_get_last_rel(),
393-
error = function(e) {
394-
rethrow_error_from_rapi(e, call)
395-
}
396-
)
397-
}
398-
399390
rethrow_rapi_rel_to_altrep <- function(rel, allow_materialization = TRUE, call = parent.frame(2)) {
400391
rlang::try_fetch(
401392
# duckplyr compat
@@ -585,7 +576,6 @@ rethrow_restore <- function() {
585576
rethrow_rapi_rel_from_sql <<- rapi_rel_from_sql
586577
rethrow_rapi_rel_from_table <<- rapi_rel_from_table
587578
rethrow_rapi_rel_from_table_function <<- rapi_rel_from_table_function
588-
rethrow_rapi_get_last_rel <<- rapi_get_last_rel
589579
rethrow_rapi_rel_to_altrep <<- rapi_rel_to_altrep
590580
rethrow_rapi_rel_from_altrep_df <<- rapi_rel_from_altrep_df
591581
rethrow_rapi_release <<- rapi_release

src/cpp11.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,6 @@ extern "C" SEXP _duckdb_rapi_rel_from_table_function(SEXP con, SEXP function_nam
315315
END_CPP11
316316
}
317317
// reltoaltrep.cpp
318-
SEXP rapi_get_last_rel();
319-
extern "C" SEXP _duckdb_rapi_get_last_rel() {
320-
BEGIN_CPP11
321-
return cpp11::as_sexp(rapi_get_last_rel());
322-
END_CPP11
323-
}
324-
// reltoaltrep.cpp
325318
SEXP rapi_rel_to_altrep(duckdb::rel_extptr_t rel, bool allow_materialization);
326319
extern "C" SEXP _duckdb_rapi_rel_to_altrep(SEXP rel, SEXP allow_materialization) {
327320
BEGIN_CPP11
@@ -452,7 +445,6 @@ static const R_CallMethodDef CallEntries[] = {
452445
{"_duckdb_rapi_expr_set_alias", (DL_FUNC) &_duckdb_rapi_expr_set_alias, 2},
453446
{"_duckdb_rapi_expr_tostring", (DL_FUNC) &_duckdb_rapi_expr_tostring, 1},
454447
{"_duckdb_rapi_expr_window", (DL_FUNC) &_duckdb_rapi_expr_window, 9},
455-
{"_duckdb_rapi_get_last_rel", (DL_FUNC) &_duckdb_rapi_get_last_rel, 0},
456448
{"_duckdb_rapi_get_null_SEXP_ptr", (DL_FUNC) &_duckdb_rapi_get_null_SEXP_ptr, 0},
457449
{"_duckdb_rapi_get_substrait", (DL_FUNC) &_duckdb_rapi_get_substrait, 3},
458450
{"_duckdb_rapi_get_substrait_json", (DL_FUNC) &_duckdb_rapi_get_substrait_json, 3},

src/include/rapi.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ struct RStrings {
165165
SEXP ImportSchema_sym;
166166
SEXP ImportRecordBatch_sym;
167167
SEXP ImportRecordBatchReader_sym;
168-
SEXP materialize_sym;
168+
SEXP materialize_callback_sym;
169+
SEXP materialize_message_sym;
169170
SEXP duckdb_row_names_sym;
170171
SEXP duckdb_vector_sym;
171172

src/reltoaltrep.cpp

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#define __STDC_FORMAT_MACROS
22

3-
#include "httplib.hpp"
43
#include "rapi.hpp"
54
#include "typesr.hpp"
65
#include "reltoaltrep.hpp"
76
#include "signal.hpp"
87
#include "cpp11/declarations.hpp"
98

9+
#include "httplib.hpp"
1010
#include <cinttypes>
1111

1212
using namespace duckdb;
@@ -85,8 +85,8 @@ struct AltrepRelationWrapper {
8585
return GetFromExternalPtr<AltrepRelationWrapper>(x);
8686
}
8787

88-
AltrepRelationWrapper(duckdb::shared_ptr<Relation> rel_p, bool allow_materialization_)
89-
: allow_materialization(allow_materialization_), rel(rel_p) {
88+
AltrepRelationWrapper(rel_extptr_t rel_, bool allow_materialization_)
89+
: allow_materialization(allow_materialization_), rel_eptr(rel_), rel(rel_->rel) {
9090
}
9191

9292
bool HasQueryResult() const {
@@ -99,12 +99,15 @@ struct AltrepRelationWrapper {
9999
cpp11::stop("Materialization is disabled, use collect() or as_tibble() to materialize");
100100
}
101101

102-
auto option = Rf_GetOption(RStrings::get().materialize_sym, R_BaseEnv);
103-
if (option != R_NilValue && !Rf_isNull(option) && LOGICAL_ELT(option, 0) == true) {
104-
Rprintf("duckplyr: materializing, review details with duckplyr::last_rel()\n");
102+
auto materialize_callback = Rf_GetOption(RStrings::get().materialize_callback_sym, R_BaseEnv);
103+
if (Rf_isFunction(materialize_callback)) {
104+
sexp call = Rf_lang2(materialize_callback, rel_eptr);
105+
Rf_eval(call, R_BaseEnv);
106+
}
107+
else if (Rf_isLogical(materialize_callback) && Rf_length(materialize_callback) == 1 && LOGICAL_ELT(materialize_callback, 0) == true) {
108+
// Legacy
109+
Rprintf("duckplyr: materializing\n");
105110
}
106-
107-
last_rel = rel;
108111

109112
ScopedInterruptHandler signal_handler(rel->context.GetContext());
110113

@@ -142,14 +145,11 @@ struct AltrepRelationWrapper {
142145

143146
bool allow_materialization;
144147

148+
rel_extptr_t rel_eptr;
145149
duckdb::shared_ptr<Relation> rel;
146150
duckdb::unique_ptr<QueryResult> res;
147-
148-
static duckdb::shared_ptr<Relation> last_rel;
149151
};
150152

151-
duckdb::shared_ptr<Relation> AltrepRelationWrapper::last_rel;
152-
153153
struct AltrepRownamesWrapper {
154154

155155
AltrepRownamesWrapper(duckdb::shared_ptr<AltrepRelationWrapper> rel_p) : rel(rel_p) {
@@ -338,17 +338,12 @@ static R_altrep_class_t LogicalTypeToAltrepType(const LogicalType &type) {
338338
}
339339
}
340340

341-
[[cpp11::register]] SEXP rapi_get_last_rel() {
342-
auto last_rel = AltrepRelationWrapper::last_rel;
343-
return sexp(make_external_prot<RelationWrapper>("duckdb_relation", R_NilValue, std::move(last_rel)));
344-
}
345-
346341
[[cpp11::register]] SEXP rapi_rel_to_altrep(duckdb::rel_extptr_t rel, bool allow_materialization) {
347342
D_ASSERT(rel && rel->rel);
348343
auto drel = rel->rel;
349344
auto ncols = drel->Columns().size();
350345

351-
auto relation_wrapper = make_shared_ptr<AltrepRelationWrapper>(drel, allow_materialization);
346+
auto relation_wrapper = make_shared_ptr<AltrepRelationWrapper>(rel, allow_materialization);
352347

353348
cpp11::writable::list data_frame;
354349
data_frame.reserve(ncols);

src/utils.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ RStrings::RStrings() {
8080
ImportRecordBatch_sym = Rf_install("ImportRecordBatch");
8181
ImportRecordBatchReader_sym = Rf_install("ImportRecordBatchReader");
8282
Table__from_record_batches_sym = Rf_install("Table__from_record_batches");
83-
materialize_sym = Rf_install("duckdb.materialize_message");
83+
materialize_message_sym = Rf_install("duckdb.materialize_message");
84+
materialize_callback_sym = Rf_install("duckdb.materialize_callback");
8485
duckdb_row_names_sym = Rf_install("duckdb_row_names");
8586
duckdb_vector_sym = Rf_install("duckdb_vector");
8687
}

tests/testthat/_snaps/relational.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@
6464
Error:
6565
! expr_comparison: Invalid comparison operator
6666

67+
# the altrep-conversion for relations works
68+
69+
Code
70+
last_rel
71+
Message
72+
DuckDB Relation:
73+
---------------------
74+
--- Relation Tree ---
75+
---------------------
76+
r_dataframe_scan(0x...)
77+
78+
---------------------
79+
-- Result Columns --
80+
---------------------
81+
- Sepal.Length (DOUBLE)
82+
- Sepal.Width (DOUBLE)
83+
- Petal.Length (DOUBLE)
84+
- Petal.Width (DOUBLE)
85+
- Species (VARCHAR)
86+
87+
6788
# rel_tostring()
6889

6990
Code

tests/testthat/test-relational.R

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ test_that("we can cast R strings to DuckDB strings", {
162162
})
163163

164164
test_that("the altrep-conversion for relations works", {
165+
local_edition(3)
166+
167+
n_callback <- 0
168+
last_rel <- NULL
169+
rlang::local_options(duckdb.materialize_callback = function(rel) {
170+
n_callback <<- n_callback + 1
171+
last_rel <<- rel
172+
})
173+
165174
iris$Species <- as.character(iris$Species)
166175
rel <- rel_from_df(con, iris)
167176
df <- rel_to_altrep(rel)
@@ -170,8 +179,15 @@ test_that("the altrep-conversion for relations works", {
170179
expect_true(any(grepl("DUCKDB_ALTREP_REL_VECTOR", inspect_output, fixed = TRUE)))
171180
expect_true(any(grepl("DUCKDB_ALTREP_REL_ROWNAMES", inspect_output, fixed = TRUE)))
172181
expect_false(df_is_materialized(df))
182+
expect_equal(n_callback, 0)
173183
dim(df)
174184
expect_true(df_is_materialized(df))
185+
186+
expect_snapshot(transform = function(x) gsub("0x[0-9a-f]+", "0x...", x), {
187+
last_rel
188+
})
189+
190+
expect_equal(n_callback, 1)
175191
expect_equal(iris, df)
176192
})
177193

0 commit comments

Comments
 (0)