diff --git a/NAMESPACE b/NAMESPACE index e3dfa45cd9..40e8fa7a9e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,12 +31,15 @@ export(capacity) export(cell_order) export(cell_val_num) export(check) +export(completedBatched) export(config) +export(createBatched) export(datatype) export(datetimes_as_int64) export(dimensions) export(domain) export(extended) +export(fetchBatched) export(filter_list) export(fromDataFrame) export(fromMatrix) @@ -70,6 +73,7 @@ export(selected_ranges) export(set_allocation_size_preference) export(set_max_chunk_size) export(set_return_as_preference) +export(statusBatched) export(strings_as_factors) export(tdb_collect) export(tdb_filter) diff --git a/R/RcppExports.R b/R/RcppExports.R index 7d85e3741f..3c6343b40f 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -25,6 +25,10 @@ libtiledb_query_import_buffer <- function(ctx, query, name, arrowpointers) { .Call(`_tiledb_libtiledb_query_import_buffer`, ctx, query, name, arrowpointers) } +makeQueryWrapper <- function(qp) { + .Call(`_tiledb_makeQueryWrapper`, qp) +} + libtiledb_query_set_coordinates <- function(query, coords, dtype) { .Call(`_tiledb_libtiledb_query_set_coordinates`, query, coords, dtype) } diff --git a/R/batchedQuery.R b/R/batchedQuery.R new file mode 100644 index 0000000000..4f340a1ee7 --- /dev/null +++ b/R/batchedQuery.R @@ -0,0 +1,389 @@ + +#' Create a \sQuote{batched} query object +#' +#' Batched queries return an initial result set even when it is incomplete. Where +#' the normal retrieval process will loop in place to complete a (potentially large) +#' result set, this function will return a result (which may be part of a larger +#' result set) allowing the user to assemble all part. +#' +#' The \code{tiledb_array} object can be parameterised as usual. +#' +#' @param x A \code{tiledb_array} object +#' @return A \code{batchedquery} object, that is a list containing an external pointer +#' to a TileDB Query object along with other support variables used by \code{fetchBatched} +#' @export +createBatched <- function(x) { + ## add defaults, shortcut for now + i <- j <- k <- NULL + verbose <- getOption("verbose", FALSE) + + ## ## deal with possible n-dim indexing + ## ndlist <- nd_index_from_syscall(sys.call(), parent.frame()) + ## if (length(ndlist) >= 0) { + ## if (length(ndlist) >= 1 && !is.null(ndlist[[1]])) i <- ndlist[[1]] + ## if (length(ndlist) >= 2 && !is.null(ndlist[[2]])) j <- ndlist[[2]] + ## if (length(ndlist) >= 3 && !is.null(ndlist[[3]])) k <- ndlist[[3]] + ## if (length(ndlist) >= 4) message("Indices beyond the third dimension not supported in [i,j,k] form. Use selected_ranges().") + ## } + + ctx <- x@ctx + uri <- x@uri + sel <- x@attrs + sch <- tiledb::schema(x) + dom <- tiledb::domain(sch) + layout <- x@query_layout + asint64 <- x@datetimes_as_int64 + enckey <- x@encryption_key + tstamp <- x@timestamp + + sparse <- libtiledb_array_schema_sparse(sch@ptr) + + dims <- tiledb::dimensions(dom) + dimnames <- sapply(dims, function(d) libtiledb_dim_get_name(d@ptr)) + dimtypes <- sapply(dims, function(d) libtiledb_dim_get_datatype(d@ptr)) + dimvarnum <- sapply(dims, function(d) libtiledb_dim_get_cell_val_num(d@ptr)) + dimnullable <- sapply(dims, function(d) FALSE) + + attrs <- tiledb::attrs(schema(x)) + attrnames <- unname(sapply(attrs, function(a) libtiledb_attribute_get_name(a@ptr))) + attrtypes <- unname(sapply(attrs, function(a) libtiledb_attribute_get_type(a@ptr))) + attrvarnum <- unname(sapply(attrs, function(a) libtiledb_attribute_get_cell_val_num(a@ptr))) + attrnullable <- unname(sapply(attrs, function(a) libtiledb_attribute_get_nullable(a@ptr))) + if (length(sel)==1 && is.na(sel[1])) { # special case of NA selecting no attrs + attrnames <- character() + attrtypes <- character() + attrvarnum <- integer() + attrnullable <- logical() + } + + if (length(sel) != 0 && !any(is.na(sel))) { + ind <- match(sel, attrnames) + if (length(ind) == 0) { + stop("Only non-existing columns selected.", call.=FALSE) + } + attrnames <- attrnames[ind] + attrtypes <- attrtypes[ind] + attrvarnum <- attrvarnum[ind] + attrnullable <- attrnullable[ind] + } + + if (x@extended) { # if true return dimensions and attributes + allnames <- c(dimnames, attrnames) + alltypes <- c(dimtypes, attrtypes) + allvarnum <- c(dimvarnum, attrvarnum) + allnullable <- c(dimnullable, attrnullable) + } else { # otherwise only return attributes + allnames <- attrnames + alltypes <- attrtypes + allvarnum <- attrvarnum + allnullable <- attrnullable + } + + ## A preference can be set in a local per-user configuration file; if no value + ## is set a fallback from the TileDB config object is used. + memory_budget <- get_allocation_size_preference() + ##if (verbose) message("Memory budget set to ", memory_budget, " bytes or ", memory_budget/8, " rows") + + if (length(enckey) > 0) { + if (length(tstamp) > 0) { + arrptr <- libtiledb_array_open_at_with_key(ctx@ptr, uri, "READ", enckey, tstamp) + } else { + arrptr <- libtiledb_array_open_with_key(ctx@ptr, uri, "READ", enckey) + } + } else { + if (length(tstamp) > 0) { + arrptr <- libtiledb_array_open_at(ctx@ptr, uri, "READ", tstamp) + } else { + arrptr <- libtiledb_array_open(ctx@ptr, uri, "READ") + } + } + if (length(x@timestamp_start) > 0) { + arrptr <- libtiledb_array_set_open_timestamp_start(arrptr, x@timestamp_start) + } + if (length(x@timestamp_end) > 0) { + arrptr <- libtiledb_array_set_open_timestamp_end(arrptr, x@timestamp_end) + } + if (length(x@timestamp_start) > 0 || length(x@timestamp_end) > 0) { + arrptr <- libtiledb_array_reopen(arrptr) + } + + ## helper function to sweep over names and types of domain + getDomain <- function(nm, tp) { + if (tp %in% c("ASCII", "CHAR")) { + libtiledb_array_get_non_empty_domain_var_from_name(arrptr, nm) + } else { + libtiledb_array_get_non_empty_domain_from_name(arrptr, nm, tp) + } + } + nonemptydom <- mapply(getDomain, dimnames, dimtypes, SIMPLIFY=FALSE) + + ## open query + qryptr <- libtiledb_query(ctx@ptr, arrptr, "READ") + if (length(layout) > 0) libtiledb_query_set_layout(qryptr, layout) + + + ## ranges seem to interfere with the byte/element adjustment below so set up toggle + rangeunset <- TRUE + + ## ensure selected_ranges, if submitted, is of correct length + if (length(x@selected_ranges) != 0 && + length(x@selected_ranges) != length(dimnames) && + is.null(names(x@selected_ranges))) { + stop(paste0("If ranges are selected by index alone (and not named), ", + "one is required for each dimension."), call. = FALSE) + } + + ## expand a shorter-but-named selected_ranges list + if ( (length(x@selected_ranges) < length(dimnames)) + && (!is.null(names(x@selected_ranges))) ) { + fulllist <- vector(mode="list", length=length(dimnames)) + ind <- match(names(x@selected_ranges), dimnames) + if (any(is.na(ind))) stop("Name for selected ranges does not match dimension names.") + for (ii in seq_len(length(ind))) { + fulllist[[ ind[ii] ]] <- x@selected_ranges[[ii]] + } + x@selected_ranges <- fulllist + } + + ## selected_ranges may be in different order than dimnames, so reorder if need be + if ((length(x@selected_ranges) == length(dimnames)) + && (!is.null(names(x@selected_ranges))) + && (!identical(names(x@selected_ranges), dimnames))) { + x@selected_ranges <- x@selected_ranges[dimnames] + } + + ## if selected_ranges is still an empty list, make it an explicit one + if (length(x@selected_ranges) == 0) { + x@selected_ranges <- vector(mode="list", length=length(dimnames)) + } + + if (!is.null(i)) { + if (!is.null(x@selected_ranges[[1]])) { + stop("Cannot set both 'i' and first element of 'selected_ranges'.", call. = FALSE) + } + x@selected_ranges[[1]] <- i + } + + if (!is.null(j)) { + if (!is.null(x@selected_ranges[[2]])) { + stop("Cannot set both 'j' and second element of 'selected_ranges'.", call. = FALSE) + } + x@selected_ranges[[2]] <- j + } + + if (!is.null(k)) { + if (!is.null(x@selected_ranges[[3]])) { + stop("Cannot set both 'k' and second element of 'selected_ranges'.", call. = FALSE) + } + x@selected_ranges[[3]] <- k + } + ## (i,j,k) are now done and transferred to x@select_ranges + + + ## if ranges selected, use those + for (k in seq_len(length(x@selected_ranges))) { + if (is.null(x@selected_ranges[[k]])) { + ##cat("Adding null dim", k, "on", dimtypes[k], "\n") + vec <- .map2integer64(nonemptydom[[k]], dimtypes[k]) + if (vec[1] != 0 && vec[2] != 0) { # corner case of A[] on empty array + qryptr <- libtiledb_query_add_range_with_type(qryptr, k-1, dimtypes[k], vec[1], vec[2]) + rangeunset <- FALSE + } + } else if (is.null(nrow(x@selected_ranges[[k]]))) { + ##cat("Adding nrow null dim", k, "on", dimtypes[k], "\n") + vec <- x@selected_ranges[[k]] + vec <- .map2integer64(vec, dimtypes[k]) + qryptr <- libtiledb_query_add_range_with_type(qryptr, k-1, dimtypes[k], min(vec), max(vec)) + rangeunset <- FALSE + } else { + ##cat("Adding non-zero dim", k, "on", dimtypes[k], "\n") + m <- x@selected_ranges[[k]] + for (i in seq_len(nrow(m))) { + vec <- .map2integer64(c(m[i,1], m[i,2]), dimtypes[k]) + qryptr <- libtiledb_query_add_range_with_type(qryptr, k-1, dimtypes[k], vec[1], vec[2]) + } + rangeunset <- FALSE + } + } + + buflist <- vector(mode="list", length=length(allnames)) + + #if (!qryinit) { + ## retrieve est_result_size + getEstimatedSize <- function(name, varnum, nullable, qryptr, datatype) { + if (is.na(varnum) && !nullable) + res <- libtiledb_query_get_est_result_size_var(qryptr, name)[1] + else if (is.na(varnum) && nullable) + res <- libtiledb_query_get_est_result_size_var_nullable(qryptr, name)[1] + else if (!is.na(varnum) && !nullable) + res <- libtiledb_query_get_est_result_size(qryptr, name) + else if (!is.na(varnum) && nullable) + res <- libtiledb_query_get_est_result_size_nullable(qryptr, name)[1] + if (rangeunset && tiledb::tiledb_version(TRUE) >= "2.2.0") { + sz <- tiledb_datatype_string_to_sizeof(datatype) + res <- res / sz + } + res + } + ressizes <- mapply(getEstimatedSize, allnames, allvarnum, allnullable, alltypes, + MoreArgs=list(qryptr=qryptr), SIMPLIFY=TRUE) + ## ensure > 0 for correct handling of zero-length outputs, ensure respecting memory budget + resrv <- max(1, min(memory_budget/8, ressizes)) + ## allocate and set buffers + getBuffer <- function(name, type, varnum, nullable, resrv, qryptr, arrptr) { + if (is.na(varnum)) { + if (type %in% c("CHAR", "ASCII", "UTF8")) { + #if (verbose) message("Allocating with ", resrv, " and ", memory_budget) + buf <- libtiledb_query_buffer_var_char_alloc_direct(resrv, memory_budget, nullable) + qryptr <- libtiledb_query_set_buffer_var_char(qryptr, name, buf) + buf + } else { + message("Non-char var.num columns are not currently supported.") + } + } else { + #if (verbose) message("Allocating with ", resrv, " and ", memory_budget) + buf <- libtiledb_query_buffer_alloc_ptr(type, resrv, nullable) + qryptr <- libtiledb_query_set_buffer_ptr(qryptr, name, buf) + buf + } + } + buflist <- mapply(getBuffer, allnames, alltypes, allvarnum, allnullable, + MoreArgs=list(resrv=resrv, qryptr=qryptr, arrptr=arrptr), + SIMPLIFY=FALSE) + + ## if we have a query condition, apply it + if (isTRUE(x@query_condition@init)) { + qryptr <- libtiledb_query_set_condition(qryptr, x@query_condition@ptr) + } + #} + + + res <- list(qryptr, allnames, allvarnum, alltypes, allnullable, buflist) + class(res) <- "batchedquery" + res +} + + +#' Run a \sQuote{batched} query +#' +#' Batched queries return an initial result set even when it is incomplete. Where +#' the normal retrieval process will loop in place to complete a (potentially large) +#' result set, this function will return a result (which may be part of a larger +#' result set) allowing the user to assemble all part. +#' +#' The \code{tiledb_array} object can be parameterised as usual. +#' +#' @param x A \code{tiledb_array} object +#' @param obj A \code{batchedquery} object as returned by \code{createBatched} +#' @return A data.frame object with the (potentially partial) result of a +#' batched query +#' @export +fetchBatched <- function(x, obj) { + stopifnot("The 'x' argument must be a 'tiledb_array'" = is(x, "tiledb_array"), + "The 'obj' argument must be 'batchedquery' object" = inherits(obj, "batchedquery")) + qryptr <- obj[[1]] + allnames <- obj[[2]] + allvarnum <- obj[[3]] + alltypes <- obj[[4]] + allnullable <- obj[[5]] + buflist <- obj[[6]] + + asint64 <- x@datetimes_as_int64 + + verbose <- TRUE + + ## fire off query + qryptr <- libtiledb_query_submit(qryptr) + + ## check status + status <- libtiledb_query_status(qryptr) + ##if (status != "COMPLETE") warning("Query returned '", status, "'.", call. = FALSE) + + ## close array + if (status == "COMPLETE") { + arrptr <- x@ptr + libtiledb_array_close(arrptr) + .pkgenv[["query_status"]] <- status + finished <- TRUE + } + + ## retrieve actual result size (from fixed size element columns) + getResultSize <- function(name, varnum, qryptr) { + if (is.na(varnum)) # symbols come up with higher count + libtiledb_query_result_buffer_elements(qryptr, name, 0) + else + libtiledb_query_result_buffer_elements(qryptr, name) + } + estsz <- mapply(getResultSize, allnames, allvarnum, MoreArgs=list(qryptr=qryptr), SIMPLIFY=TRUE) + if (any(!is.na(estsz))) { + resrv <- max(estsz, na.rm=TRUE) + } else { + resrv <- resrv/8 # character case where bytesize of offset vector was used + } + ##if (verbose) message("Expected size ", resrv) + ## Permit one pass to allow zero-row schema read + #if (resrv == 0 && counter > 1L) { + #finished <- TRUE + ##if (verbose) message("Breaking loop at zero length expected") + #if (status != "COMPLETE") warning("Query returned '", status, "'.", call. = FALSE) + #.pkgenv[["query_status"]] <- status + #break + #} + ## get results + getResult <- function(buf, name, varnum, resrv, qryptr) { + has_dumpbuffers <- length(x@dumpbuffers) > 0 + if (is.na(varnum)) { + vec <- libtiledb_query_result_buffer_elements_vec(qryptr, name) + if (has_dumpbuffers) { + vlcbuf_to_shmem(x@dumpbuffers, name, buf, vec) + } + libtiledb_query_get_buffer_var_char(buf, vec[1], vec[2])[,1] + } else { + if (has_dumpbuffers) { + vecbuf_to_shmem(x@dumpbuffers, name, buf, resrv) + } + libtiledb_query_get_buffer_ptr(buf, asint64) + } + } + reslist <- mapply(getResult, buflist, allnames, allvarnum, + MoreArgs=list(resrv=resrv, qryptr=qryptr), SIMPLIFY=FALSE) + ## convert list into data.frame (cheaply) and subset + res <- data.frame(reslist)[seq_len(resrv),,drop=FALSE] + colnames(res) <- allnames + ##if (verbose) cat("Retrieved ", paste(dim(res), collapse="x"), "...\n") + ##overallresults[[counter]] <- res + ##counter <- counter + 1L + + res +} + + +#' Return \sQuote{batched} status +#' +#' Batched queries return an initial result set even when it is incomplete. Where +#' the normal retrieval process will loop in place to complete a (potentially large) +#' result set, this function will return a result (which may be part of a larger +#' result set) allowing the user to assemble all part. +#' +#' @param obj A list object as returned by \code{createBatched} +#' @return The Query status as a character variable +#' @export +statusBatched <- function(obj) { + stopifnot("The 'obj' argument must be 'batchedquery' object" = inherits(obj, "batchedquery")) + libtiledb_query_status(obj[[1]]) +} + +#' Check \sQuote{batched} query for completion +#' +#' Batched queries return an initial result set even when it is incomplete. Where +#' the normal retrieval process will loop in place to complete a (potentially large) +#' result set, this function will return a result (which may be part of a larger +#' result set) allowing the user to assemble all part. +#' +#' @param obj A list object as returned by \code{createBatched} +#' @return A logical value to indicated if the query completed +#' @export +completedBatched <- function(obj) { + stopifnot("The 'obj' argument must be 'batchedquery' object" = inherits(obj, "batchedquery")) + libtiledb_query_status(obj[[1]]) == "COMPLETE" +} diff --git a/inst/tinytest/test_tiledbarray.R b/inst/tinytest/test_tiledbarray.R index 7d0731e1f0..f2ee742b49 100644 --- a/inst/tinytest/test_tiledbarray.R +++ b/inst/tinytest/test_tiledbarray.R @@ -1434,3 +1434,18 @@ set_allocation_size_preference(256) # too low for penguins to return somethi array <- tiledb_array(uri, as.data.frame=TRUE) expect_warning(res <- array[]) # warning emitted expect_equal(attr(res, "query_status"), "INCOMPLETE") # and query status reported + + +## check for batched operation +set_allocation_size_preference(1024) +arr <- tiledb_array(uri, as.data.frame=TRUE) +lst <- tiledb:::createBatched(arr) +res1 <- tiledb:::fetchBatched(arr, lst) +expect_false(completedBatched(lst)) +res2 <- tiledb:::fetchBatched(arr, lst) +expect_false(completedBatched(lst)) +res3 <- tiledb:::fetchBatched(arr, lst) +expect_false(completedBatched(lst)) +res4 <- tiledb:::fetchBatched(arr, lst) +expect_true(completedBatched(lst)) +expect_equal(nrow(res1) + nrow(res2) + nrow(res3) + nrow(res4), 344) diff --git a/man/completedBatched.Rd b/man/completedBatched.Rd new file mode 100644 index 0000000000..b3e28f968b --- /dev/null +++ b/man/completedBatched.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/batchedQuery.R +\name{completedBatched} +\alias{completedBatched} +\title{Check \sQuote{batched} query for completion} +\usage{ +completedBatched(obj) +} +\arguments{ +\item{obj}{A list object as returned by \code{createBatched}} +} +\value{ +A logical value to indicated if the query completed +} +\description{ +Batched queries return an initial result set even when it is incomplete. Where +the normal retrieval process will loop in place to complete a (potentially large) +result set, this function will return a result (which may be part of a larger +result set) allowing the user to assemble all part. +} diff --git a/man/createBatched.Rd b/man/createBatched.Rd new file mode 100644 index 0000000000..1bf8f7fc15 --- /dev/null +++ b/man/createBatched.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/batchedQuery.R +\name{createBatched} +\alias{createBatched} +\title{Create a \sQuote{batched} query object} +\usage{ +createBatched(x) +} +\arguments{ +\item{x}{A \code{tiledb_array} object} +} +\value{ +A \code{batchedquery} object, that is a list containing an external pointer +to a TileDB Query object along with other support variables used by \code{fetchBatched} +} +\description{ +Batched queries return an initial result set even when it is incomplete. Where +the normal retrieval process will loop in place to complete a (potentially large) +result set, this function will return a result (which may be part of a larger +result set) allowing the user to assemble all part. +} +\details{ +The \code{tiledb_array} object can be parameterised as usual. +} diff --git a/man/fetchBatched.Rd b/man/fetchBatched.Rd new file mode 100644 index 0000000000..40d5f72f7e --- /dev/null +++ b/man/fetchBatched.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/batchedQuery.R +\name{fetchBatched} +\alias{fetchBatched} +\title{Run a \sQuote{batched} query} +\usage{ +fetchBatched(x, obj) +} +\arguments{ +\item{x}{A \code{tiledb_array} object} + +\item{obj}{A \code{batchedquery} object as returned by \code{createBatched}} +} +\value{ +A data.frame object with the (potentially partial) result of a +batched query +} +\description{ +Batched queries return an initial result set even when it is incomplete. Where +the normal retrieval process will loop in place to complete a (potentially large) +result set, this function will return a result (which may be part of a larger +result set) allowing the user to assemble all part. +} +\details{ +The \code{tiledb_array} object can be parameterised as usual. +} diff --git a/man/statusBatched.Rd b/man/statusBatched.Rd new file mode 100644 index 0000000000..d7f1eb7c18 --- /dev/null +++ b/man/statusBatched.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/batchedQuery.R +\name{statusBatched} +\alias{statusBatched} +\title{Return \sQuote{batched} status} +\usage{ +statusBatched(obj) +} +\arguments{ +\item{obj}{A list object as returned by \code{createBatched}} +} +\value{ +The Query status as a character variable +} +\description{ +Batched queries return an initial result set even when it is incomplete. Where +the normal retrieval process will loop in place to complete a (potentially large) +result set, this function will return a result (which may be part of a larger +result set) allowing the user to assemble all part. +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 56471f0d72..4b61510b1a 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -79,6 +79,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// makeQueryWrapper +SEXP makeQueryWrapper(SEXP qp); +RcppExport SEXP _tiledb_makeQueryWrapper(SEXP qpSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< SEXP >::type qp(qpSEXP); + rcpp_result_gen = Rcpp::wrap(makeQueryWrapper(qp)); + return rcpp_result_gen; +END_RCPP +} // libtiledb_query_set_coordinates XPtr libtiledb_query_set_coordinates(XPtr query, SEXP coords, std::string dtype); RcppExport SEXP _tiledb_libtiledb_query_set_coordinates(SEXP querySEXP, SEXP coordsSEXP, SEXP dtypeSEXP) { @@ -3111,6 +3122,7 @@ static const R_CallMethodDef CallEntries[] = { {"_tiledb_delete_arrow_schema_from_xptr", (DL_FUNC) &_tiledb_delete_arrow_schema_from_xptr, 1}, {"_tiledb_libtiledb_query_export_buffer", (DL_FUNC) &_tiledb_libtiledb_query_export_buffer, 3}, {"_tiledb_libtiledb_query_import_buffer", (DL_FUNC) &_tiledb_libtiledb_query_import_buffer, 4}, + {"_tiledb_makeQueryWrapper", (DL_FUNC) &_tiledb_makeQueryWrapper, 1}, {"_tiledb_libtiledb_query_set_coordinates", (DL_FUNC) &_tiledb_libtiledb_query_set_coordinates, 3}, {"_tiledb_libtiledb_coords", (DL_FUNC) &_tiledb_libtiledb_coords, 0}, {"_tiledb_tiledb_datatype_string_to_sizeof", (DL_FUNC) &_tiledb_tiledb_datatype_string_to_sizeof, 1}, diff --git a/src/batched.cpp b/src/batched.cpp new file mode 100644 index 0000000000..6922ef0fc1 --- /dev/null +++ b/src/batched.cpp @@ -0,0 +1,19 @@ + +#include "libtiledb.h" +#include "tiledb_version.h" + +class QueryWrapper { +public: + QueryWrapper(SEXP qp): qryptr(qp), init(true) {}; +private: + SEXP qryptr; + bool init; +}; + +// [[Rcpp::export]] +SEXP makeQueryWrapper(SEXP qp) { + //alternate form: Rcpp::XPtr makeQueryWrapper(SEXP qp) { + QueryWrapper* qwp = new QueryWrapper(qp); + Rcpp::XPtr ptr(qwp); + return ptr; +}