Skip to content

Commit

Permalink
Merged origin/main into databricks-write
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Feb 14, 2024
2 parents 3563fa7 + 9732723 commit 9bc84f6
Show file tree
Hide file tree
Showing 25 changed files with 409 additions and 104 deletions.
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ Config/testthat/edition: 3
Config/testthat/parallel: TRUE
Encoding: UTF-8
Language: en-gb
Roxygen: {library(tidyr); list(markdown = TRUE)}
RoxygenNote: 7.2.3
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.1
Collate:
'db-sql.R'
'utils-check.R'
Expand Down Expand Up @@ -101,6 +101,7 @@ Collate:
'dbplyr.R'
'explain.R'
'ident.R'
'import-standalone-s3-register.R'
'join-by-compat.R'
'join-cols-compat.R'
'lazy-join-query.R'
Expand Down
15 changes: 13 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,18 @@ S3method(dim,tbl_lazy)
S3method(dimnames,tbl_lazy)
S3method(distinct,tbl_lazy)
S3method(do,tbl_sql)
S3method(dplyr::group_by_drop_default,tbl_lazy)
S3method(dplyr::intersect,tbl_lazy)
S3method(dplyr::setdiff,OraConnection)
S3method(dplyr::setdiff,tbl_Oracle)
S3method(dplyr::setdiff,tbl_lazy)
S3method(dplyr::union,tbl_lazy)
S3method(dplyr::union_all,tbl_lazy)
S3method(escape,"NULL")
S3method(escape,Date)
S3method(escape,POSIXt)
S3method(escape,blob)
S3method(escape,character)
S3method(escape,data.frame)
S3method(escape,dbplyr_catalog)
S3method(escape,dbplyr_schema)
S3method(escape,dbplyr_table_ident)
Expand All @@ -131,7 +137,6 @@ S3method(escape,integer)
S3method(escape,integer64)
S3method(escape,list)
S3method(escape,logical)
S3method(escape,reactivevalues)
S3method(escape,sql)
S3method(explain,tbl_sql)
S3method(flatten_query,base_query)
Expand Down Expand Up @@ -422,6 +427,12 @@ S3method(tbl,src_dbi)
S3method(tbl_format_header,tbl_sql)
S3method(tbl_sum,tbl_sql)
S3method(tbl_vars,tbl_lazy)
S3method(tidyr::complete,tbl_lazy)
S3method(tidyr::expand,tbl_lazy)
S3method(tidyr::fill,tbl_lazy)
S3method(tidyr::pivot_longer,tbl_lazy)
S3method(tidyr::pivot_wider,tbl_lazy)
S3method(tidyr::replace_na,tbl_lazy)
S3method(tidyselect_data_has_predicates,tbl_lazy)
S3method(tidyselect_data_proxy,tbl_lazy)
S3method(transmute,tbl_lazy)
Expand Down
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

* The databricks backend now supports creating non-temporary tables too (#1418).

* Clearer error if you attempt to embed non-atomic vectors inside of a generated
query (#1368).

* `x$name` never attempts to evaluate `name` (#1368).

* `rows_patch(in_place = FALSE)` now works when more than one column should be
patched (@gorcha, #1443).

* Namespaced dplyr calls now error if the function doesn't exist, or
a translation is not available (#1426).

Expand Down
8 changes: 7 additions & 1 deletion R/backend-.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ base_scalar <- sql_translator(
}
},

`$` = sql_infix(".", pad = FALSE),
`$` = function(x, name) {
if (!is.sql(x)) {
cli_abort("{.code $} can only subset database columns, not inlined values.")
}
glue_sql2(sql_current_con(), "{x}.{.col name}")
},

`[[` = function(x, i) {
# `x` can be a table, column or even an expression (e.g. for json)
i <- enexpr(i)
Expand Down
4 changes: 2 additions & 2 deletions R/backend-oracle.R
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ sql_values_subquery.Oracle <- function(con, df, types, lvl = 0, ...) {
sql_values_subquery_union(con, df, types = types, lvl = lvl, from = "DUAL")
}

# registered onLoad located in the zzz.R script
#' @exportS3Method dplyr::setdiff
setdiff.tbl_Oracle <- function(x, y, copy = FALSE, ...) {
# Oracle uses MINUS instead of EXCEPT for this operation:
# https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries004.htm
Expand Down Expand Up @@ -260,7 +260,7 @@ sql_query_save.OraConnection <- sql_query_save.Oracle
#' @export
sql_values_subquery.OraConnection <- sql_values_subquery.Oracle

# registered onLoad located in the zzz.R script
#' @exportS3Method dplyr::setdiff
setdiff.OraConnection <- setdiff.tbl_Oracle

#' @export
Expand Down
10 changes: 0 additions & 10 deletions R/escape.R
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,6 @@ escape.list <- function(x, parens = TRUE, collapse = ", ", con = NULL) {
sql_vector(pieces, parens, collapse, con = con)
}

#' @export
escape.data.frame <- function(x, parens = TRUE, collapse = ", ", con = NULL) {
error_embed("a data.frame", "df$x")
}

#' @export
escape.reactivevalues <- function(x, parens = TRUE, collapse = ", ", con = NULL) {
error_embed("shiny inputs", "input$x")
}

# Also used in default_ops() for reactives
error_embed <- function(type, expr) {
cli_abort(c(
Expand Down
187 changes: 187 additions & 0 deletions R/import-standalone-s3-register.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Standalone file: do not edit by hand
# Source: <https://github.com/r-lib/rlang/blob/main/R/standalone-s3-register.R>
# ----------------------------------------------------------------------
#
# ---
# repo: r-lib/rlang
# file: standalone-s3-register.R
# last-updated: 2022-08-29
# license: https://unlicense.org
# ---
#
# nocov start

#' Register a method for a suggested dependency
#'
#' Generally, the recommended way to register an S3 method is to use the
#' `S3Method()` namespace directive (often generated automatically by the
#' `@export` roxygen2 tag). However, this technique requires that the generic
#' be in an imported package, and sometimes you want to suggest a package,
#' and only provide a method when that package is loaded. `s3_register()`
#' can be called from your package's `.onLoad()` to dynamically register
#' a method only if the generic's package is loaded.
#'
#' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating
#' class creation in a vignette, since method lookup no longer always involves
#' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect
#' by using "delayed method registration", i.e. placing the following in your
#' `NAMESPACE` file:
#'
#' ```
#' if (getRversion() >= "3.6.0") {
#' S3method(package::generic, class)
#' }
#' ```
#'
#' @section Usage in other packages:
#' To avoid taking a dependency on vctrs, you copy the source of
#' [`s3_register()`](https://github.com/r-lib/rlang/blob/main/R/standalone-s3-register.R)
#' into your own package. It is licensed under the permissive
#' [unlicense](https://choosealicense.com/licenses/unlicense/) to make it
#' crystal clear that we're happy for you to do this. There's no need to include
#' the license or even credit us when using this function.
#'
#' @param generic Name of the generic in the form `"pkg::generic"`.
#' @param class Name of the class
#' @param method Optionally, the implementation of the method. By default,
#' this will be found by looking for a function called `generic.class`
#' in the package environment.
#' @examples
#' # A typical use case is to dynamically register tibble/pillar methods
#' # for your class. That way you avoid creating a hard dependency on packages
#' # that are not essential, while still providing finer control over
#' # printing when they are used.
#'
#' .onLoad <- function(...) {
#' s3_register("pillar::pillar_shaft", "vctrs_vctr")
#' s3_register("tibble::type_sum", "vctrs_vctr")
#' }
#' @keywords internal
#' @noRd
s3_register <- function(generic, class, method = NULL) {
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)

pieces <- strsplit(generic, "::")[[1]]
stopifnot(length(pieces) == 2)
package <- pieces[[1]]
generic <- pieces[[2]]

caller <- parent.frame()

get_method_env <- function() {
top <- topenv(caller)
if (isNamespace(top)) {
asNamespace(environmentName(top))
} else {
caller
}
}
get_method <- function(method) {
if (is.null(method)) {
get(paste0(generic, ".", class), envir = get_method_env())
} else {
method
}
}

register <- function(...) {
envir <- asNamespace(package)

# Refresh the method each time, it might have been updated by
# `devtools::load_all()`
method_fn <- get_method(method)
stopifnot(is.function(method_fn))


# Only register if generic can be accessed
if (exists(generic, envir)) {
registerS3method(generic, class, method_fn, envir = envir)
} else if (identical(Sys.getenv("NOT_CRAN"), "true")) {
warn <- .rlang_s3_register_compat("warn")

warn(c(
sprintf(
"Can't find generic `%s` in package %s to register S3 method.",
generic,
package
),
"i" = "This message is only shown to developers using devtools.",
"i" = sprintf("Do you need to update %s to the latest version?", package)
))
}
}

# Always register hook in case package is later unloaded & reloaded
setHook(packageEvent(package, "onLoad"), function(...) {
register()
})

# For compatibility with R < 4.1.0 where base isn't locked
is_sealed <- function(pkg) {
identical(pkg, "base") || environmentIsLocked(asNamespace(pkg))
}

# Avoid registration failures during loading (pkgload or regular).
# Check that environment is locked because the registering package
# might be a dependency of the package that exports the generic. In
# that case, the exports (and the generic) might not be populated
# yet (#1225).
if (isNamespaceLoaded(package) && is_sealed(package)) {
register()
}

invisible()
}

.rlang_s3_register_compat <- function(fn, try_rlang = TRUE) {
# Compats that behave the same independently of rlang's presence
out <- switch(
fn,
is_installed = return(function(pkg) requireNamespace(pkg, quietly = TRUE))
)

# Only use rlang if it is fully loaded (#1482)
if (try_rlang &&
requireNamespace("rlang", quietly = TRUE) &&
environmentIsLocked(asNamespace("rlang"))) {
switch(
fn,
is_interactive = return(rlang::is_interactive)
)

# Make sure rlang knows about "x" and "i" bullets
if (utils::packageVersion("rlang") >= "0.4.2") {
switch(
fn,
abort = return(rlang::abort),
warn = return((rlang::warn)),
inform = return(rlang::inform)
)
}
}

# Fall back to base compats

is_interactive_compat <- function() {
opt <- getOption("rlang_interactive")
if (!is.null(opt)) {
opt
} else {
interactive()
}
}

format_msg <- function(x) paste(x, collapse = "\n")
switch(
fn,
is_interactive = return(is_interactive_compat),
abort = return(function(msg) stop(format_msg(msg), call. = FALSE)),
warn = return(function(msg) warning(format_msg(msg), call. = FALSE)),
inform = return(function(msg) message(format_msg(msg)))
)

stop(sprintf("Internal error in rlang shims: Unknown function `%s()`.", fn))
}

# nocov end
6 changes: 5 additions & 1 deletion R/rows.R
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,11 @@ rows_patch.tbl_lazy <- function(x,
)

patch_columns_y <- paste0(new_columns, "...y")
patch_quos <- lapply(new_columns, function(.x) quo(coalesce(!!sym(.x), !!sym(patch_columns_y)))) %>%
patch_quos <-
lapply(
seq_along(new_columns),
function(.x) quo(coalesce(!!sym(new_columns[.x]), !!sym(patch_columns_y[.x])))
) %>%
rlang::set_names(new_columns)
if (is_empty(new_columns)) {
patched <- to_patch
Expand Down
2 changes: 1 addition & 1 deletion R/tbl-lazy.R
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ groups.tbl_lazy <- function(x) {
}

# nocov start
# Manually registered in zzz.R
#' @exportS3Method dplyr::group_by_drop_default
group_by_drop_default.tbl_lazy <- function(x) {
TRUE
}
Expand Down
25 changes: 23 additions & 2 deletions R/tidyeval.R
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ partial_eval <- function(call, data, env = caller_env(), vars = NULL, error_call
data <- lazy_frame(!!!rep_named(data, list(logical())))
}

if (is_atomic(call) || is_null(call) || blob::is_blob(call)) {
if (is_sql_literal(call)) {
call
} else if (is_symbol(call)) {
partial_eval_sym(call, data, env)
Expand All @@ -89,6 +89,10 @@ partial_eval <- function(call, data, env = caller_env(), vars = NULL, error_call
}
}

is_sql_literal <- function(x) {
is_atomic(x) || is_null(x) || blob::is_blob(x)
}

capture_dot <- function(.data, x) {
partial_eval(enquo(x), data = .data)
}
Expand Down Expand Up @@ -153,7 +157,20 @@ partial_eval_sym <- function(sym, data, env) {
if (name %in% vars) {
sym
} else if (env_has(env, name, inherit = TRUE)) {
eval_bare(sym, env)
val <- eval_bare(sym, env)

# Handle common failure modes
if (inherits(val, "data.frame")) {
error_embed("a data.frame", paste0(name, "$x"))
} else if (inherits(val, "reactivevalues")) {
error_embed("shiny inputs", paste0(name, "$x"))
}

if (is_sql_literal(val)) {
unname(val)
} else {
error_embed(obj_type_friendly(val), name)
}
} else {
cli::cli_abort(
"Object {.var {name}} not found.",
Expand Down Expand Up @@ -213,6 +230,10 @@ partial_eval_call <- function(call, data, env) {
eval_bare(call[[2]], env)
} else if (is_call(call, "remote")) {
call[[2]]
} else if (is_call(call, "$")) {
# Only the 1st argument is evaluated
call[[2]] <- partial_eval(call[[2]], data = data, env = env)
call
} else {
call[-1] <- lapply(call[-1], partial_eval, data = data, env = env)
call
Expand Down
Loading

0 comments on commit 9bc84f6

Please sign in to comment.