Skip to content

Commit

Permalink
Merged origin/main into list-access
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Jan 10, 2024
2 parents 0364249 + c50f26d commit 85eb94f
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 12 deletions.
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@

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

* `db_sql_render()` correctly passes on `...` when re-calling with
`sql_options` set (#1394).

* `-1 + x` is now translated correctly (#1420).

* SQL server: clear error if you attempt to use `n_distinct()` in `mutate()`
or `filter()` (#1366).

* Add translations for clock functions `add_years()`, `add_days()`,
`date_build()`, `get_year()`, `get_month()`, `get_day()`,
and `base::difftime()` on SQL server, Redshift, Snowflake, and Postgres.

* SQL server: `filter()` does a better job of converting logical vectors
from bit to boolean (@ejneer, #1288).

Expand Down
45 changes: 43 additions & 2 deletions R/backend-mssql.R
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,41 @@ simulate_mssql <- function(version = "15.0") {
sql_expr(DATEPART(QUARTER, !!x))
}
},

# clock ---------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(DAY, !!n, !!x))
},
add_years = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(YEAR, !!n, !!x))
},
date_build = function(year, month = 1L, day = 1L, ..., invalid = NULL) {
sql_expr(DATEFROMPARTS(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(DATEPART('year', !!x))
},
get_month = function(x) {
sql_expr(DATEPART('month', !!x))
},
get_day = function(x) {
sql_expr(DATEPART('day', !!x))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(DATEDIFF(day, !!time1, !!time2))
}
)

if (mssql_version(con) >= "11.0") { # MSSQL 2012
Expand Down Expand Up @@ -434,7 +469,13 @@ simulate_mssql <- function(version = "15.0") {
},
all = mssql_bit_int_bit(win_aggregate("MIN")),
any = mssql_bit_int_bit(win_aggregate("MAX")),
row_number = win_rank("ROW_NUMBER", empty_order = TRUE)
row_number = win_rank("ROW_NUMBER", empty_order = TRUE),

n_distinct = function(x) {
cli_abort(
"No translation available in `mutate()`/`filter()` for SQL server."
)
}
)

)}
Expand Down Expand Up @@ -607,7 +648,7 @@ mssql_update_where_clause <- function(qry) {
}

qry$where <- lapply(
qry$where,
qry$where,
function(x) set_expr(x, bit_to_boolean(get_expr(x)))
)
qry
Expand Down
26 changes: 25 additions & 1 deletion R/backend-oracle.R
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,31 @@ sql_translation.Oracle <- function(con) {

# lubridate --------------------------------------------------------------
today = function() sql_expr(TRUNC(CURRENT_TIMESTAMP)),
now = function() sql_expr(CURRENT_TIMESTAMP)
now = function() sql_expr(CURRENT_TIMESTAMP),

# clock ------------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
sql_expr((!!x + NUMTODSINTERVAL(!!n, 'day')))
},
add_years = function(x, n, ...) {
check_dots_empty()
sql_expr((!!x + NUMTODSINTERVAL(!!n * 365.25, 'day')))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(CEIL(CAST(!!time2 %AS% DATE) - CAST(!!time1 %AS% DATE)))
}

),
base_odbc_agg,
base_odbc_win
Expand Down
35 changes: 35 additions & 0 deletions R/backend-postgres.R
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,41 @@ sql_translation.PqConnection <- function(con) {
)
sql_expr(DATE_TRUNC(!!unit, !!x))
},

# clock ---------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
glue_sql2(sql_current_con(), "({.col x} + {.val n}*INTERVAL'1 day')")
},
add_years = function(x, n, ...) {
check_dots_empty()
glue_sql2(sql_current_con(), "({.col x} + {.val n}*INTERVAL'1 year')")
},
date_build = function(year, month = 1L, day = 1L, ..., invalid = NULL) {
sql_expr(make_date(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(date_part('year', !!x))
},
get_month = function(x) {
sql_expr(date_part('month', !!x))
},
get_day = function(x) {
sql_expr(date_part('day', !!x))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr((CAST(!!time2 %AS% DATE) - CAST(!!time1 %AS% DATE)))
},
),
sql_translator(.parent = base_agg,
cor = sql_aggregate_2("CORR"),
Expand Down
35 changes: 35 additions & 0 deletions R/backend-redshift.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,41 @@ sql_translation.RedshiftConnection <- function(con) {
str_replace = sql_not_supported("str_replace"),
str_replace_all = function(string, pattern, replacement) {
sql_expr(REGEXP_REPLACE(!!string, !!pattern, !!replacement))
},

# clock ---------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(DAY, !!n, !!x))
},
add_years = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(YEAR, !!n, !!x))
},
date_build = function(year, month = 1L, day = 1L, ..., invalid = NULL) {
glue_sql2(sql_current_con(), "TO_DATE(CAST({.val year} AS TEXT) || '-' CAST({.val month} AS TEXT) || '-' || CAST({.val day} AS TEXT)), 'YYYY-MM-DD')")
},
get_year = function(x) {
sql_expr(DATE_PART('year', !!x))
},
get_month = function(x) {
sql_expr(DATE_PART('month', !!x))
},
get_day = function(x) {
sql_expr(DATE_PART('day', !!x))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(DATEDIFF(day, !!time1, !!time2))
}
),
sql_translator(.parent = postgres$aggregate,
Expand Down
35 changes: 35 additions & 0 deletions R/backend-snowflake.R
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,41 @@ sql_translation.Snowflake <- function(con) {
)
sql_expr(DATE_TRUNC(!!unit, !!x))
},
# clock ---------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(DAY, !!n, !!x))
},
add_years = function(x, n, ...) {
check_dots_empty()
sql_expr(DATEADD(YEAR, !!n, !!x))
},
date_build = function(year, month = 1L, day = 1L, ..., invalid = NULL) {
# https://docs.snowflake.com/en/sql-reference/functions/date_from_parts
sql_expr(DATE_FROM_PARTS(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(DATE_PART('year', !!x))
},
get_month = function(x) {
sql_expr(DATE_PART('month', !!x))
},
get_day = function(x) {
sql_expr(DATE_PART('day', !!x))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(DATEDIFF(day, !!time1, !!time2))
},
# LEAST / GREATEST on Snowflake will not respect na.rm = TRUE by default (similar to Oracle/Access)
# https://docs.snowflake.com/en/sql-reference/functions/least
# https://docs.snowflake.com/en/sql-reference/functions/greatest
Expand Down
37 changes: 36 additions & 1 deletion R/backend-spark-sql.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,42 @@ simulate_spark_sql <- function() simulate_dbi("Spark SQL")
#' @export
`sql_translation.Spark SQL` <- function(con) {
sql_variant(
base_odbc_scalar,
sql_translator(.parent = base_odbc_scalar,
# clock ---------------------------------------------------------------
add_days = function(x, n, ...) {
check_dots_empty()
sql_expr(date_add(!!x, !!n))
},
add_years = function(x, n, ...) {
check_dots_empty()
sql_expr(add_months(!!!x, !!n*12))
},
date_build = function(year, month = 1L, day = 1L, ..., invalid = NULL) {
sql_expr(make_date(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(date_part('YEAR', !!x))
},
get_month = function(x) {
sql_expr(date_part('MONTH', !!x))
},
get_day = function(x) {
sql_expr(date_part('DAY', !!x))
},

difftime = function(time1, time2, tz, units = "days") {

if (!missing(tz)) {
cli::cli_abort("The {.arg tz} argument is not supported for SQL backends.")
}

if (units[1] != "days") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(datediff(!!time2, !!time1))
}
),
sql_translator(.parent = base_odbc_agg,
var = sql_aggregate("VARIANCE", "var"),
quantile = sql_quantile("PERCENTILE"),
Expand Down
6 changes: 3 additions & 3 deletions R/db.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,20 @@ sql_join_suffix.DBIConnection <- function(con, suffix, ...) {
db_sql_render <- function(con, sql, ..., cte = FALSE, sql_options = NULL) {
check_bool(cte)
if (cte) {
lifecycle::deprecate_soft(
lifecycle::deprecate_soft(
when = "2.4.0",
what = "db_sql_render(cte)",
with = I("db_sql_render(sql_options = sql_options(cte = TRUE))")
)
sql_options <- sql_options %||% sql_options(cte = TRUE)
out <- db_sql_render(con, sql, sql_options = sql_options)
out <- db_sql_render(con, sql, ..., sql_options = sql_options)
return(out)
}

if (is.null(sql_options)) {
sql_options <- sql_options()

out <- db_sql_render(con, sql, sql_options = sql_options)
out <- db_sql_render(con, sql, ..., sql_options = sql_options)
return(out)
}

Expand Down
2 changes: 1 addition & 1 deletion R/tbl-sql.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#' multiple `tbl` objects.
#' @param check_from Check if `from` is likely misspecified SQL or a table in a schema.
tbl_sql <- function(subclass, src, from, ..., vars = NULL, check_from = TRUE) {
check_dots_used()
# Can't use check_dots_used(), #1429
check_character(vars, allow_null = TRUE)

from <- as_from(from)
Expand Down
2 changes: 1 addition & 1 deletion R/tidyeval.R
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ partial_eval_sym <- function(sym, data, env) {
}

is_namespaced_dplyr_call <- function(call) {
packages <- c("base", "dplyr", "stringr", "lubridate")
packages <- c("base", "dplyr", "stringr", "lubridate", "clock")
is_symbol(call[[1]], "::") && is_symbol(call[[2]], packages)
}

Expand Down
5 changes: 3 additions & 2 deletions R/translate-sql-helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,11 @@ sql_infix <- function(f, pad = TRUE) {
escape_infix_expr <- function(xq, x, escape_unary_minus = FALSE) {
infix_calls <- c("+", "-", "*", "/", "%%", "^")
is_infix <- is_call(xq, infix_calls, n = 2)
is_unary_minus <- escape_unary_minus && is_call(xq, "-", n = 1)
is_unary_minus <- escape_unary_minus &&
is_call(xq, "-", n = 1) && !is_atomic(x, n = 1)

if (is_infix || is_unary_minus) {
enpared <- glue_sql2(sql_current_con(), "({x})")
enpared <- glue_sql2(sql_current_con(), "({.val x})")
return(enpared)
}

Expand Down
8 changes: 8 additions & 0 deletions tests/testthat/_snaps/backend-mssql.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
i Use a combination of `distinct()` and `mutate()` for the same result:
`mutate(<col> = median(x, na.rm = TRUE)) %>% distinct(<col>)`

# custom window functions translated correctly

Code
test_translate_sql(n_distinct(x), vars_group = "x")
Condition
Error in `n_distinct()`:
! No translation available in `mutate()`/`filter()` for SQL server.

# custom lubridate functions translated correctly

Code
Expand Down
1 change: 1 addition & 0 deletions tests/testthat/test-backend-.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ test_that("unary plus works for non-numeric expressions", {
test_that("unary minus flips sign of number", {
local_con(simulate_dbi())
expect_equal(test_translate_sql(-10L), sql("-10"))
expect_equal(test_translate_sql(-10L + x), sql("-10 + `x`"))
expect_equal(test_translate_sql(x == -10), sql('`x` = -10.0'))
expect_equal(test_translate_sql(x %in% c(-1L, 0L)), sql('`x` IN (-1, 0)'))
})
Expand Down
Loading

0 comments on commit 85eb94f

Please sign in to comment.