diff --git a/r/R/dplyr-funcs-simple.R b/r/R/dplyr-funcs-simple.R index 1f17bf216d7a..e556c7418de1 100644 --- a/r/R/dplyr-funcs-simple.R +++ b/r/R/dplyr-funcs-simple.R @@ -188,6 +188,14 @@ common_type <- function(exprs) { } cast_or_parse <- function(x, type) { + # A null scalar (e.g. a bare `NA`, which is logical) carries no data, so + # skip the value cast and just create a null of the target type. This + # avoids unsupported casts like bool -> date32 (GH-38358). Only Scalars + # have `is_valid`; Arrays (e.g. from the `%in%` binding) take the normal path. + if (inherits(x, "Scalar") && !x$is_valid) { + return(Scalar$create(NULL)$cast(type)) + } + to_type_id <- type$id if (to_type_id %in% c(Type[["DECIMAL32"]], Type[["DECIMAL64"]], Type[["DECIMAL128"]], Type[["DECIMAL256"]])) { # TODO: determine the minimum size of decimal (or integer) required to diff --git a/r/tests/testthat/test-dplyr-funcs-conditional.R b/r/tests/testthat/test-dplyr-funcs-conditional.R index f7d5b4d6b95f..e746a845fa28 100644 --- a/r/tests/testthat/test-dplyr-funcs-conditional.R +++ b/r/tests/testthat/test-dplyr-funcs-conditional.R @@ -876,3 +876,26 @@ test_that("recode_values()", { class = "arrow_not_supported" ) }) + +test_that("if_else with logical NA and a date/timestamp column", { + # GH-38358: a bare `NA` is logical and can't be cast to date32, so + # if_else(bool, bool, date32) had no matching kernel + date_tbl <- tibble::tibble( + date = as.Date(c("2013-01-01", "2013-01-02", "2033-01-03", "2013-01-04")), + ts = as.POSIXct( + c("2013-01-01 01:00:00", "2013-01-02 02:00:00", "2033-01-03 03:00:00", "2013-01-04 04:00:00"), + tz = "UTC" + ) + ) + + compare_dplyr_binding( + .input |> + mutate( + date2 = if_else(date > as.Date("2014-01-01"), NA, date), + date3 = if_else(date > as.Date("2014-01-01"), date, NA), + ts2 = if_else(ts > as.POSIXct("2014-01-01", tz = "UTC"), NA, ts) + ) |> + collect(), + date_tbl + ) +})