Skip to content

Commit 8af4046

Browse files
[r] Add check_only support for domain/shape updates (#3400)
* Add `check_only` flag to shape/domain upgraders/resizers, for parity with Python * unit-test coverage * Apply suggestions from code review * more code-review feedback * more code-review feedback * code-review feedback [skip ci] Co-authored-by: Paul Hoffman <[email protected]> * apis/r/DESCRIPTION apis/r/NEWS.md --------- Co-authored-by: Paul Hoffman <[email protected]>
1 parent ff8c19d commit 8af4046

File tree

8 files changed

+161
-56
lines changed

8 files changed

+161
-56
lines changed

apis/r/DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Description: Interface for working with 'TileDB'-based Stack of Matrices,
66
like those commonly used for single cell data analysis. It is documented at
77
<https://github.com/single-cell-data>; a formal specification available is at
88
<https://github.com/single-cell-data/SOMA/blob/main/abstract_specification.md>.
9-
Version: 1.15.99.19
9+
Version: 1.15.99.20
1010
Authors@R: c(
1111
person(given = "Aaron", family = "Wolen",
1212
role = c("cre", "aut"), email = "[email protected]",

apis/r/NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* Update docstrings for `domain` argument to `create` [#3396](https://github.com/single-cell-data/TileDB-SOMA/pull/3396)
2828
* Vignette for new-shape feature [#3302](https://github.com/single-cell-data/TileDB-SOMA/pull/3302)
2929
* Fix blockwise iterator + re-indexer to return re-indexed shape instead of full domain
30+
* Add `check_only` support for domain/shape updates [#3400](https://github.com/single-cell-data/TileDB-SOMA/pull/3400)
3031

3132
# tiledbsoma 1.14.5
3233

apis/r/R/RcppExports.R

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,20 +238,20 @@ c_schema <- function(uri, ctxxp) {
238238
.Call(`_tiledbsoma_c_schema`, uri, ctxxp)
239239
}
240240

241-
resize <- function(uri, new_shape, function_name_for_messages, ctxxp) {
242-
invisible(.Call(`_tiledbsoma_resize`, uri, new_shape, function_name_for_messages, ctxxp))
241+
resize <- function(uri, new_shape, function_name_for_messages, check_only, ctxxp) {
242+
.Call(`_tiledbsoma_resize`, uri, new_shape, function_name_for_messages, check_only, ctxxp)
243243
}
244244

245245
resize_soma_joinid_shape <- function(uri, new_shape, function_name_for_messages, ctxxp) {
246246
invisible(.Call(`_tiledbsoma_resize_soma_joinid_shape`, uri, new_shape, function_name_for_messages, ctxxp))
247247
}
248248

249-
tiledbsoma_upgrade_shape <- function(uri, new_shape, function_name_for_messages, ctxxp) {
250-
invisible(.Call(`_tiledbsoma_tiledbsoma_upgrade_shape`, uri, new_shape, function_name_for_messages, ctxxp))
249+
tiledbsoma_upgrade_shape <- function(uri, new_shape, function_name_for_messages, check_only, ctxxp) {
250+
.Call(`_tiledbsoma_tiledbsoma_upgrade_shape`, uri, new_shape, function_name_for_messages, check_only, ctxxp)
251251
}
252252

253-
upgrade_or_change_domain <- function(uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, ctxxp) {
254-
invisible(.Call(`_tiledbsoma_upgrade_or_change_domain`, uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, ctxxp))
253+
upgrade_or_change_domain <- function(uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, check_only, ctxxp) {
254+
.Call(`_tiledbsoma_upgrade_or_change_domain`, uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, check_only, ctxxp)
255255
}
256256

257257
c_update_dataframe_schema <- function(uri, ctxxp, column_names_to_drop, add_cols_types, add_cols_enum_value_types, add_cols_enum_ordered) {

apis/r/R/SOMADataFrame.R

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -472,26 +472,35 @@ SOMADataFrame <- R6::R6Class(
472472
#' @param new_domain A named list, keyed by index-column name, with values
473473
#' being two-element vectors containing the desired lower and upper bounds
474474
#' for the domain.
475-
#' @return No return value
476-
tiledbsoma_upgrade_domain = function(new_domain) {
477-
# stopifnot("'new_domain' must be CODE ME UP PLZ" = ...
475+
#' @param check_only If true, does not apply the operation, but only reports
476+
#' whether it would have succeeded.
477+
#' @return No return value if `check_only` is `FALSE`. If `check_only` is `TRUE`,
478+
#' returns the empty string if no error is detected, else a description of the error.
479+
tiledbsoma_upgrade_domain = function(new_domain, check_only = FALSE) {
478480
# Checking slotwise new shape >= old shape, and <= max_shape, is already
479481
# done in libtiledbsoma
480482

481483
pyarrow_domain_table <- private$upgrade_or_change_domain_helper(
482484
new_domain, "tiledbsoma_upgrade_domain"
483485
)
484486

485-
invisible(
486-
upgrade_or_change_domain(
487-
self$uri,
488-
FALSE,
489-
pyarrow_domain_table$array,
490-
pyarrow_domain_table$schema,
491-
.name_of_function(),
492-
private$.soma_context
493-
)
487+
reason_string <- upgrade_or_change_domain(
488+
self$uri,
489+
FALSE,
490+
pyarrow_domain_table$array,
491+
pyarrow_domain_table$schema,
492+
.name_of_function(),
493+
check_only,
494+
private$.soma_context
494495
)
496+
497+
if (isTRUE(check_only)) {
498+
return(reason_string)
499+
}
500+
501+
# The return value from upgrade_or_change_domain without check_only is
502+
# always "", or it raises an error trying.
503+
return(invisible(NULL))
495504
},
496505

497506
#' @description Allows you to set the domain of a `SOMADataFrame`, when the
@@ -506,26 +515,35 @@ SOMADataFrame <- R6::R6Class(
506515
#' @param new_domain A named list, keyed by index-column name, with values
507516
#' being two-element vectors containing the desired lower and upper bounds
508517
#' for the domain.
509-
#' @return No return value
510-
change_domain = function(new_domain) {
511-
# stopifnot("'new_domain' must be CODE ME UP PLZ" = ...
518+
#' @param check_only If true, does not apply the operation, but only reports
519+
#' whether it would have succeeded.
520+
#' @return No return value if `check_only` is `FALSE`. If `check_only` is `TRUE`,
521+
#' returns the empty string if no error is detected, else a description of the error.
522+
change_domain = function(new_domain, check_only = FALSE) {
512523
# Checking slotwise new shape >= old shape, and <= max_shape, is already
513524
# done in libtiledbsoma
514525

515526
pyarrow_domain_table <- private$upgrade_or_change_domain_helper(
516527
new_domain, tiledbsoma_upgrade_domain
517528
)
518529

519-
invisible(
520-
upgrade_or_change_domain(
530+
reason_string <- upgrade_or_change_domain(
521531
self$uri,
522532
TRUE,
523533
pyarrow_domain_table$array,
524534
pyarrow_domain_table$schema,
525535
.name_of_function(),
536+
check_only,
526537
private$.soma_context
527-
)
528538
)
539+
540+
if (isTRUE(check_only)) {
541+
return(reason_string)
542+
}
543+
544+
# The return value from upgrade_or_change_domain without check_only is
545+
# always "", or it raises an error trying.
546+
return(invisible(NULL))
529547
}
530548
),
531549
private = list(

apis/r/R/SOMANDArrayBase.R

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,27 @@ SOMANDArrayBase <- R6::R6Class(
102102
#' doesn't already have a shape: in that case please call
103103
#' `tiledbsoma_upgrade_shape`. (lifecycle: maturing)
104104
#' @param new_shape A vector of integerish, of the same length as the array's `ndim`.
105-
#' @return No return value
106-
resize = function(new_shape) {
105+
#' @param check_only If true, does not apply the operation, but only reports
106+
#' whether it would have succeeded.
107+
#' @return No return value if `check_only` is `FALSE`. If `check_only` is `TRUE`,
108+
#' returns the empty string if no error is detected, else a description of the error.
109+
resize = function(new_shape, check_only = FALSE) {
107110
stopifnot(
108111
"'new_shape' must be a vector of integerish values, of the same length as maxshape" =
109112
rlang::is_integerish(new_shape, n = self$ndim()) ||
110113
(bit64::is.integer64(new_shape) && length(new_shape) == self$ndim())
111114
)
112115
# Checking slotwise new shape >= old shape, and <= max_shape, is already done in libtiledbsoma
113-
resize(self$uri, new_shape, .name_of_function(), private$.soma_context)
116+
117+
reason_string <- resize(self$uri, new_shape, .name_of_function(), check_only, private$.soma_context)
118+
119+
if (isTRUE(check_only)) {
120+
return(reason_string)
121+
}
122+
123+
# The return value from resize without check_only is always "", or it
124+
# raises an error trying.
125+
return(invisible(NULL))
114126
},
115127

116128
#' @description Allows the array to have a resizeable shape as described in the
@@ -121,15 +133,26 @@ SOMANDArrayBase <- R6::R6Class(
121133
#' be used for subsequent resizes on any array which already has upgraded shape.
122134
#' (lifecycle: maturing)
123135
#' @param shape A vector of integerish, of the same length as the array's `ndim`.
124-
#' @return No return value
125-
tiledbsoma_upgrade_shape = function(shape) {
136+
#' @param check_only If true, does not apply the operation, but only reports
137+
#' whether it would have succeeded.
138+
#' @return No return value if `check_only` is `FALSE`. If `check_only` is `TRUE`,
139+
#' returns the empty string if no error is detected, else a description of the error.
140+
tiledbsoma_upgrade_shape = function(shape, check_only = FALSE) {
126141
stopifnot(
127142
"'shape' must be a vector of integerish values, of the same length as maxshape" =
128143
rlang::is_integerish(shape, n = self$ndim()) ||
129144
(bit64::is.integer64(shape) && length(shape) == self$ndim())
130145
)
131146
# Checking slotwise new shape >= old shape, and <= max_shape, is already done in libtiledbsoma
132-
tiledbsoma_upgrade_shape(self$uri, shape, .name_of_function(), private$.soma_context)
147+
148+
reason_string <- tiledbsoma_upgrade_shape(self$uri, shape, .name_of_function(), check_only, private$.soma_context)
149+
if (isTRUE(check_only)) {
150+
return(reason_string)
151+
}
152+
153+
# The return value from tiledbsoma_upgrade_shape without check_only is
154+
# always "", or it raises an error trying.
155+
return(invisible(NULL))
133156
}
134157
),
135158
private = list(

apis/r/src/RcppExports.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -553,16 +553,18 @@ BEGIN_RCPP
553553
END_RCPP
554554
}
555555
// resize
556-
void resize(const std::string& uri, Rcpp::NumericVector new_shape, std::string function_name_for_messages, Rcpp::XPtr<somactx_wrap_t> ctxxp);
557-
RcppExport SEXP _tiledbsoma_resize(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP function_name_for_messagesSEXP, SEXP ctxxpSEXP) {
556+
std::string resize(const std::string& uri, Rcpp::NumericVector new_shape, std::string function_name_for_messages, bool check_only, Rcpp::XPtr<somactx_wrap_t> ctxxp);
557+
RcppExport SEXP _tiledbsoma_resize(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP function_name_for_messagesSEXP, SEXP check_onlySEXP, SEXP ctxxpSEXP) {
558558
BEGIN_RCPP
559+
Rcpp::RObject rcpp_result_gen;
559560
Rcpp::RNGScope rcpp_rngScope_gen;
560561
Rcpp::traits::input_parameter< const std::string& >::type uri(uriSEXP);
561562
Rcpp::traits::input_parameter< Rcpp::NumericVector >::type new_shape(new_shapeSEXP);
562563
Rcpp::traits::input_parameter< std::string >::type function_name_for_messages(function_name_for_messagesSEXP);
564+
Rcpp::traits::input_parameter< bool >::type check_only(check_onlySEXP);
563565
Rcpp::traits::input_parameter< Rcpp::XPtr<somactx_wrap_t> >::type ctxxp(ctxxpSEXP);
564-
resize(uri, new_shape, function_name_for_messages, ctxxp);
565-
return R_NilValue;
566+
rcpp_result_gen = Rcpp::wrap(resize(uri, new_shape, function_name_for_messages, check_only, ctxxp));
567+
return rcpp_result_gen;
566568
END_RCPP
567569
}
568570
// resize_soma_joinid_shape
@@ -579,31 +581,35 @@ BEGIN_RCPP
579581
END_RCPP
580582
}
581583
// tiledbsoma_upgrade_shape
582-
void tiledbsoma_upgrade_shape(const std::string& uri, Rcpp::NumericVector new_shape, std::string function_name_for_messages, Rcpp::XPtr<somactx_wrap_t> ctxxp);
583-
RcppExport SEXP _tiledbsoma_tiledbsoma_upgrade_shape(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP function_name_for_messagesSEXP, SEXP ctxxpSEXP) {
584+
std::string tiledbsoma_upgrade_shape(const std::string& uri, Rcpp::NumericVector new_shape, std::string function_name_for_messages, bool check_only, Rcpp::XPtr<somactx_wrap_t> ctxxp);
585+
RcppExport SEXP _tiledbsoma_tiledbsoma_upgrade_shape(SEXP uriSEXP, SEXP new_shapeSEXP, SEXP function_name_for_messagesSEXP, SEXP check_onlySEXP, SEXP ctxxpSEXP) {
584586
BEGIN_RCPP
587+
Rcpp::RObject rcpp_result_gen;
585588
Rcpp::RNGScope rcpp_rngScope_gen;
586589
Rcpp::traits::input_parameter< const std::string& >::type uri(uriSEXP);
587590
Rcpp::traits::input_parameter< Rcpp::NumericVector >::type new_shape(new_shapeSEXP);
588591
Rcpp::traits::input_parameter< std::string >::type function_name_for_messages(function_name_for_messagesSEXP);
592+
Rcpp::traits::input_parameter< bool >::type check_only(check_onlySEXP);
589593
Rcpp::traits::input_parameter< Rcpp::XPtr<somactx_wrap_t> >::type ctxxp(ctxxpSEXP);
590-
tiledbsoma_upgrade_shape(uri, new_shape, function_name_for_messages, ctxxp);
591-
return R_NilValue;
594+
rcpp_result_gen = Rcpp::wrap(tiledbsoma_upgrade_shape(uri, new_shape, function_name_for_messages, check_only, ctxxp));
595+
return rcpp_result_gen;
592596
END_RCPP
593597
}
594598
// upgrade_or_change_domain
595-
void upgrade_or_change_domain(const std::string& uri, bool is_change_domain, naxpArray nadimap, naxpSchema nadimsp, std::string function_name_for_messages, Rcpp::XPtr<somactx_wrap_t> ctxxp);
596-
RcppExport SEXP _tiledbsoma_upgrade_or_change_domain(SEXP uriSEXP, SEXP is_change_domainSEXP, SEXP nadimapSEXP, SEXP nadimspSEXP, SEXP function_name_for_messagesSEXP, SEXP ctxxpSEXP) {
599+
std::string upgrade_or_change_domain(const std::string& uri, bool is_change_domain, naxpArray nadimap, naxpSchema nadimsp, std::string function_name_for_messages, bool check_only, Rcpp::XPtr<somactx_wrap_t> ctxxp);
600+
RcppExport SEXP _tiledbsoma_upgrade_or_change_domain(SEXP uriSEXP, SEXP is_change_domainSEXP, SEXP nadimapSEXP, SEXP nadimspSEXP, SEXP function_name_for_messagesSEXP, SEXP check_onlySEXP, SEXP ctxxpSEXP) {
597601
BEGIN_RCPP
602+
Rcpp::RObject rcpp_result_gen;
598603
Rcpp::RNGScope rcpp_rngScope_gen;
599604
Rcpp::traits::input_parameter< const std::string& >::type uri(uriSEXP);
600605
Rcpp::traits::input_parameter< bool >::type is_change_domain(is_change_domainSEXP);
601606
Rcpp::traits::input_parameter< naxpArray >::type nadimap(nadimapSEXP);
602607
Rcpp::traits::input_parameter< naxpSchema >::type nadimsp(nadimspSEXP);
603608
Rcpp::traits::input_parameter< std::string >::type function_name_for_messages(function_name_for_messagesSEXP);
609+
Rcpp::traits::input_parameter< bool >::type check_only(check_onlySEXP);
604610
Rcpp::traits::input_parameter< Rcpp::XPtr<somactx_wrap_t> >::type ctxxp(ctxxpSEXP);
605-
upgrade_or_change_domain(uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, ctxxp);
606-
return R_NilValue;
611+
rcpp_result_gen = Rcpp::wrap(upgrade_or_change_domain(uri, is_change_domain, nadimap, nadimsp, function_name_for_messages, check_only, ctxxp));
612+
return rcpp_result_gen;
607613
END_RCPP
608614
}
609615
// c_update_dataframe_schema
@@ -834,10 +840,10 @@ static const R_CallMethodDef CallEntries[] = {
834840
{"_tiledbsoma_c_dimnames", (DL_FUNC) &_tiledbsoma_c_dimnames, 2},
835841
{"_tiledbsoma_c_attrnames", (DL_FUNC) &_tiledbsoma_c_attrnames, 2},
836842
{"_tiledbsoma_c_schema", (DL_FUNC) &_tiledbsoma_c_schema, 2},
837-
{"_tiledbsoma_resize", (DL_FUNC) &_tiledbsoma_resize, 4},
843+
{"_tiledbsoma_resize", (DL_FUNC) &_tiledbsoma_resize, 5},
838844
{"_tiledbsoma_resize_soma_joinid_shape", (DL_FUNC) &_tiledbsoma_resize_soma_joinid_shape, 4},
839-
{"_tiledbsoma_tiledbsoma_upgrade_shape", (DL_FUNC) &_tiledbsoma_tiledbsoma_upgrade_shape, 4},
840-
{"_tiledbsoma_upgrade_or_change_domain", (DL_FUNC) &_tiledbsoma_upgrade_or_change_domain, 6},
845+
{"_tiledbsoma_tiledbsoma_upgrade_shape", (DL_FUNC) &_tiledbsoma_tiledbsoma_upgrade_shape, 5},
846+
{"_tiledbsoma_upgrade_or_change_domain", (DL_FUNC) &_tiledbsoma_upgrade_or_change_domain, 7},
841847
{"_tiledbsoma_c_update_dataframe_schema", (DL_FUNC) &_tiledbsoma_c_update_dataframe_schema, 6},
842848
{"_tiledbsoma_sr_setup", (DL_FUNC) &_tiledbsoma_sr_setup, 10},
843849
{"_tiledbsoma_sr_complete", (DL_FUNC) &_tiledbsoma_sr_complete, 1},

apis/r/src/rinterface.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -418,18 +418,29 @@ SEXP c_schema(const std::string& uri, Rcpp::XPtr<somactx_wrap_t> ctxxp) {
418418
}
419419

420420
// [[Rcpp::export]]
421-
void resize(
421+
std::string resize(
422422
const std::string& uri,
423423
Rcpp::NumericVector new_shape,
424424
std::string function_name_for_messages,
425+
bool check_only,
425426
Rcpp::XPtr<somactx_wrap_t> ctxxp) {
426427
// This function is solely for SparseNDArray and DenseNDArray for which the
427428
// dims are required by the SOMA spec to be of type int64. Domain-resize for
428429
// variant-indexed dataframes is via upgrade_domain and change_domain.
429430
auto sr = tdbs::SOMAArray::open(OpenMode::write, uri, ctxxp->ctxptr);
430431
std::vector<int64_t> new_shape_i64 = i64_from_rcpp_numeric(new_shape);
431-
sr->resize(new_shape_i64, function_name_for_messages);
432+
433+
std::string retval = "";
434+
if (check_only) {
435+
auto status_and_reason = sr->can_resize(
436+
new_shape_i64, function_name_for_messages);
437+
retval = status_and_reason.second;
438+
} else {
439+
sr->resize(new_shape_i64, function_name_for_messages);
440+
}
441+
432442
sr->close();
443+
return retval;
433444
}
434445

435446
// [[Rcpp::export]]
@@ -446,29 +457,40 @@ void resize_soma_joinid_shape(
446457
}
447458

448459
// [[Rcpp::export]]
449-
void tiledbsoma_upgrade_shape(
460+
std::string tiledbsoma_upgrade_shape(
450461
const std::string& uri,
451462
Rcpp::NumericVector new_shape,
452463
std::string function_name_for_messages,
464+
bool check_only,
453465
Rcpp::XPtr<somactx_wrap_t> ctxxp) {
454466
// This function is solely for SparseNDArray and DenseNDArray for which the
455467
// dims are required by the SOMA spec to be of type int64. Domain-resize for
456468
// variant-indexed dataframes is via upgrade_domain and change_domain.
457469
auto sr = tdbs::SOMAArray::open(OpenMode::write, uri, ctxxp->ctxptr);
458470
std::vector<int64_t> new_shape_i64 = i64_from_rcpp_numeric(new_shape);
459-
sr->upgrade_shape(new_shape_i64, function_name_for_messages);
471+
472+
std::string retval = "";
473+
if (check_only) {
474+
auto status_and_reason = sr->can_upgrade_shape(
475+
new_shape_i64, function_name_for_messages);
476+
retval = status_and_reason.second;
477+
} else {
478+
sr->upgrade_shape(new_shape_i64, function_name_for_messages);
479+
}
480+
460481
sr->close();
482+
return retval;
461483
}
462484

463485
// [[Rcpp::export]]
464-
void upgrade_or_change_domain(
486+
std::string upgrade_or_change_domain(
465487
const std::string& uri,
466488
bool is_change_domain,
467489
naxpArray nadimap,
468490
naxpSchema nadimsp,
469491
std::string function_name_for_messages,
492+
bool check_only,
470493
Rcpp::XPtr<somactx_wrap_t> ctxxp) {
471-
472494
// This is pointer manipulation from R -> Rcpp SEXP -> libtiledbsoma:
473495
nanoarrow::UniqueArray apdim{nanoarrow_array_from_xptr(nadimap)};
474496
nanoarrow::UniqueSchema spdim{nanoarrow_schema_from_xptr(nadimsp)};
@@ -482,13 +504,27 @@ void upgrade_or_change_domain(
482504
tdbs::ArrowTable arrow_table(std::move(dimarr), std::move(dimsch));
483505

484506
// Now call libtiledbsoma
507+
std::string reason_string = "";
485508
auto sr = tdbs::SOMADataFrame::open(uri, OpenMode::write, ctxxp->ctxptr);
486509
if (is_change_domain) {
487-
sr->change_domain(arrow_table, function_name_for_messages);
510+
if (check_only) {
511+
auto status_and_reason = sr->can_change_domain(
512+
arrow_table, function_name_for_messages);
513+
reason_string = status_and_reason.second;
514+
} else {
515+
sr->change_domain(arrow_table, function_name_for_messages);
516+
}
488517
} else {
489-
sr->upgrade_domain(arrow_table, function_name_for_messages);
518+
if (check_only) {
519+
auto status_and_reason = sr->can_upgrade_domain(
520+
arrow_table, function_name_for_messages);
521+
reason_string = status_and_reason.second;
522+
} else {
523+
sr->upgrade_domain(arrow_table, function_name_for_messages);
524+
}
490525
}
491526
sr->close();
527+
return reason_string;
492528
}
493529

494530
// [[Rcpp::export]]

0 commit comments

Comments
 (0)