Skip to content

Commit

Permalink
Merge commit '5b07997533dcfa3773172f904551f351a0dffef3'
Browse files Browse the repository at this point in the history
#Conflicts:
#	R/tbl-sql.R
#	tests/testthat/_snaps/tbl-sql.md
#	tests/testthat/test-tbl-sql.R
  • Loading branch information
hadley committed Feb 21, 2024
2 parents 5c18776 + 5b07997 commit d287d45
Show file tree
Hide file tree
Showing 36 changed files with 436 additions and 86 deletions.
7 changes: 7 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ S3method(supports_window_clause,PqConnection)
S3method(supports_window_clause,Redshift)
S3method(supports_window_clause,RedshiftConnection)
S3method(supports_window_clause,SQLiteConnection)
S3method(table_path_components,DBIConnection)
S3method(tail,tbl_lazy)
S3method(tally,tbl_lazy)
S3method(tbl,src_dbi)
Expand All @@ -438,6 +439,7 @@ S3method(values_prepare,DBIConnection)
S3method(values_prepare,SQLiteConnection)
export("%>%")
export(as.sql)
export(as_table_path)
export(base_agg)
export(base_no_win)
export(base_odbc_agg)
Expand All @@ -446,6 +448,7 @@ export(base_odbc_win)
export(base_scalar)
export(base_win)
export(build_sql)
export(check_table_path)
export(copy_inline)
export(copy_lahman)
export(copy_nycflights13)
Expand All @@ -472,6 +475,7 @@ export(in_catalog)
export(in_schema)
export(is.ident)
export(is.sql)
export(is_table_path)
export(join_query)
export(lahman_mysql)
export(lahman_postgres)
Expand Down Expand Up @@ -579,6 +583,8 @@ export(src_memdb)
export(src_sql)
export(src_test)
export(supports_window_clause)
export(table_path_components)
export(table_path_name)
export(tbl_lazy)
export(tbl_memdb)
export(tbl_sql)
Expand Down Expand Up @@ -678,6 +684,7 @@ importFrom(dplyr,ungroup)
importFrom(dplyr,union)
importFrom(dplyr,union_all)
importFrom(glue,glue)
importFrom(lifecycle,deprecated)
importFrom(magrittr,"%>%")
importFrom(methods,initialize)
importFrom(pillar,align)
Expand Down
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# dbplyr (development version)

* dbplyr now exports some tools to work with the internal `table_path` class
which is useful for certain backends that need to work with this
data structure (#1300).

* You can once again use `NULL` on the LHS of an infix operator in order
to generate SQL with unusual syntax (#1345).

* Oracle once again translates `head()` to `FETCH FIRST`. This does require
Oracle 12c or newer, but it actually works, compared to the approach using `ROWNUM`
from #1292 (#1436).
Expand All @@ -22,6 +29,8 @@

* If you accidentally pass a named vector to any of the database identifer
functions, those names will be automatically stripped (#1404).

* `tbl_sql(check_from)` is now deprecated.

* When dbplyr creates an index on a table in a schema (e.g. `schema.table`),
it now only includes the table name in the index name, not the schema name.
Expand Down
10 changes: 5 additions & 5 deletions R/backend-mssql.R
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,13 @@ simulate_mssql <- function(version = "15.0") {
sql_expr(DATEFROMPARTS(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(DATEPART('year', !!x))
sql_expr(DATEPART(YEAR, !!x))
},
get_month = function(x) {
sql_expr(DATEPART('month', !!x))
sql_expr(DATEPART(MONTH, !!x))
},
get_day = function(x) {
sql_expr(DATEPART('day', !!x))
sql_expr(DATEPART(DAY, !!x))
},

difftime = function(time1, time2, tz, units = "days") {
Expand All @@ -383,7 +383,7 @@ simulate_mssql <- function(version = "15.0") {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

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

Expand Down Expand Up @@ -659,4 +659,4 @@ bit_to_boolean <- function(x_expr) {
}
}

utils::globalVariables(c("BIT", "CAST", "%AS%", "%is%", "convert", "DATE", "DATENAME", "DATEPART", "IIF", "NOT", "SUBSTRING", "LTRIM", "RTRIM", "CHARINDEX", "SYSDATETIME", "SECOND", "MINUTE", "HOUR", "DAY", "DAYOFWEEK", "DAYOFYEAR", "MONTH", "QUARTER", "YEAR", "BIGINT", "INT", "%AND%", "%BETWEEN%"))
utils::globalVariables(c("BIT", "CAST", "%AS%", "%is%", "convert", "DATE", "DATEADD", "DATEFROMPARTS", "DATEDIFF", "DATENAME", "DATEPART", "IIF", "NOT", "SUBSTRING", "LTRIM", "RTRIM", "CHARINDEX", "SYSDATETIME", "SECOND", "MINUTE", "HOUR", "DAY", "DAYOFWEEK", "DAYOFYEAR", "MONTH", "QUARTER", "YEAR", "BIGINT", "INT", "%AND%", "%BETWEEN%"))
2 changes: 1 addition & 1 deletion R/backend-oracle.R
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,4 @@ db_explain.OraConnection <- db_explain.Oracle
#' @export
db_supports_table_alias_with_as.OraConnection <- db_supports_table_alias_with_as.Oracle

utils::globalVariables(c("DATE", "CURRENT_TIMESTAMP", "TRUNC", "dbms_random.VALUE"))
utils::globalVariables(c("DATE", "CURRENT_TIMESTAMP", "TRUNC", "dbms_random.VALUE", "DATEDIFF", "CEIL", "NUMTODSINTERVAL"))
2 changes: 1 addition & 1 deletion R/backend-postgres-old.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ db_write_table.PostgreSQLConnection <- function(con,
# the bare table name
dbWriteTable(
con,
name = table_name(table, con),
name = table_path_name(table, con),
value = values,
field.types = types,
...,
Expand Down
2 changes: 1 addition & 1 deletion R/backend-postgres.R
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,4 @@ db_col_types.PqConnection <- function(con, table, call) {
#' @export
db_col_types.PostgreSQL <- db_col_types.PqConnection

utils::globalVariables(c("strpos", "%::%", "%FROM%", "%ILIKE%", "DATE", "EXTRACT", "TO_CHAR", "string_agg", "%~*%", "%~%", "MONTH", "DOY", "DATE_TRUNC", "INTERVAL", "FLOOR", "WEEK"))
utils::globalVariables(c("strpos", "%::%", "%FROM%", "%ILIKE%", "DATE", "EXTRACT", "TO_CHAR", "string_agg", "%~*%", "%~%", "MONTH", "DOY", "DATE_TRUNC", "INTERVAL", "FLOOR", "WEEK", "make_date", "date_part"))
4 changes: 2 additions & 2 deletions R/backend-redshift.R
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ sql_translation.RedshiftConnection <- function(con) {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(DATEDIFF(day, !!time1, !!time2))
sql_expr(DATEDIFF(DAY, !!time1, !!time2))
}
),
sql_translator(.parent = postgres$aggregate,
Expand Down Expand Up @@ -179,4 +179,4 @@ supports_window_clause.Redshift <- function(con) {
#' @export
supports_window_clause.RedshiftConnection <- supports_window_clause.Redshift

utils::globalVariables(c("REGEXP_REPLACE", "LAG", "LEAD", "LISTAGG", "float", "text"))
utils::globalVariables(c("REGEXP_REPLACE", "LAG", "LEAD", "LISTAGG", "float", "text", "DATE_PART"))
10 changes: 5 additions & 5 deletions R/backend-snowflake.R
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,13 @@ sql_translation.Snowflake <- function(con) {
sql_expr(DATE_FROM_PARTS(!!year, !!month, !!day))
},
get_year = function(x) {
sql_expr(DATE_PART('year', !!x))
sql_expr(DATE_PART(YEAR, !!x))
},
get_month = function(x) {
sql_expr(DATE_PART('month', !!x))
sql_expr(DATE_PART(MONTH, !!x))
},
get_day = function(x) {
sql_expr(DATE_PART('day', !!x))
sql_expr(DATE_PART(DAY, !!x))
},

difftime = function(time1, time2, tz, units = "days") {
Expand All @@ -243,7 +243,7 @@ sql_translation.Snowflake <- function(con) {
cli::cli_abort('The only supported value for {.arg units} on SQL backends is "days"')
}

sql_expr(DATEDIFF(day, !!time1, !!time2))
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
Expand Down Expand Up @@ -355,4 +355,4 @@ snowflake_pmin_pmax_builder <- function(dot_1, dot_2, comparison){
glue_sql2(sql_current_con(), glue("COALESCE(IFF({dot_2} {comparison} {dot_1}, {dot_2}, {dot_1}), {dot_2}, {dot_1})"))
}

utils::globalVariables(c("%REGEXP%", "DAYNAME", "DECODE", "FLOAT", "MONTHNAME", "POSITION", "trim", "LENGTH"))
utils::globalVariables(c("%REGEXP%", "DAYNAME", "DECODE", "FLOAT", "MONTHNAME", "POSITION", "trim", "LENGTH", "DATE_FROM_PARTS", "DATE_PART"))
2 changes: 1 addition & 1 deletion R/backend-spark-sql.R
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,4 @@ simulate_spark_sql <- function() simulate_dbi("Spark SQL")
table
}

utils::globalVariables("regexp_replace")
utils::globalVariables(c("regexp_replace", "date_add", "add_months", "datediff"))
4 changes: 2 additions & 2 deletions R/db-sql.R
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ sql_table_index.DBIConnection <- function(con,
table <- as_table_path(table, con)

if (is.null(name)) {
table_name <- table_name(table, con)
table_name <- table_path_name(table, con)
name <- name %||% paste0(c(table_name, columns), collapse = "_")
}
glue_sql2(
Expand Down Expand Up @@ -259,7 +259,7 @@ sql_query_wrap.DBIConnection <- function(con, from, name = NULL, ..., lvl = 0) {
glue_sql2(con, "{from}", as_sql, "{.tbl name}")
} else { # must be a table_path
if (!is.null(name)) {
table <- table_name(name, con)
table <- table_path_name(name, con)
names(from) <- as_table_path(table, con)
}
from
Expand Down
1 change: 1 addition & 0 deletions R/dbplyr.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
#' @import DBI
#' @importFrom tibble tibble as_tibble
#' @importFrom magrittr %>%
#' @importFrom lifecycle deprecated
#' @keywords internal
"_PACKAGE"
2 changes: 1 addition & 1 deletion R/lazy-join-query.R
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ sql_build.lazy_multi_join_query <- function(op, con, ..., sql_options = NULL) {
}

generate_join_table_names <- function(table_names, con) {
names <- table_name(table_names$name, con)
names <- table_path_name(table_names$name, con)
table_name_length_max <- max(nchar(names))

if (length(table_names$name) != 2) {
Expand Down
2 changes: 1 addition & 1 deletion R/query-join.R
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ sql_qualify_var <- function(con, table, var) {
var <- sql_escape_ident(con, var)

if (!is.null(table)) {
table <- table_name(table, con)
table <- table_path_name(table, con)
table <- as_table_paths(table, con)

sql(paste0(table, ".", var))
Expand Down
2 changes: 1 addition & 1 deletion R/remote.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ remote_name <- function(x, null_if_local = TRUE) {
if (is.null(con)) {
table
} else {
table_name(table, con)
table_path_name(table, con)
}
}
}
Expand Down
81 changes: 61 additions & 20 deletions R/table-name.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,34 @@ table_path <- function(x) {
# So you can do SQL(table_path("foo"))
setOldClass(c("dbplyr_table_path", "character"))


#' Table paths
#'
#' @description
#' dbplyr standardises all the ways of referring to a table (i.e. a single
#' string, a string wrapped in `I()`, a [DBI::Id()] and the results of
#' [in_schema()] and [in_catalog()]) into a table "path" of the form
#' `table`, `schema.table`, or `catalog.schema.path`. A table path is
#' always suitable for inlining into a query, so user input is quoted unless
#' it is wrapped in `I()`.
#'
#' This is primarily for internal usage, but you may need to work with it if
#' you're implementing a backend, and you need to compute with the table path,
#' not just pass it on unchanged to some other dbplyr function.
#'
#' * `is_table_path()` returns `TRUE` if the object is a `table_path`.
#' * `as_table_path()` coerces known table identifiers to a `table_path`.
#' * `check_table_path()` throws an error if the object is not a `table_path`.
#' * `table_path_name()` returns the last component of the table path (i.e.
#' the name of the table).
#' * `table_path_components()` returns a list containing the components of each
#' table path.
#'
#' A `table_path` object can technically be a vector of table paths, but
#' you will never see this in table paths constructed from user inputs.
#'
#' @keywords internal
#' @export
is_table_path <- function(x) {
inherits(x, "dbplyr_table_path")
}
Expand All @@ -36,6 +64,7 @@ print.dbplyr_table_path <- function(x, ...) {
`[.dbplyr_table_path` <- function(x, ...) {
table_path(NextMethod())
}

#' @export
`[[.dbplyr_table_path` <- function(x, ...) {
table_path(NextMethod())
Expand Down Expand Up @@ -66,35 +95,44 @@ as_table_paths <- function(x, con) {
make_table_path(x, con, collapse = FALSE)
}

# Extract just the table name from a full identifier
table_name <- function(x, con) {
x <- as_table_path(x, con)

vapply(x, FUN.VALUE = character(1), function(x) {
if (x == "") return("")
#' @export
#' @rdname is_table_path
table_path_name <- function(x, con) {
path <- as_table_path(x, con)
components <- table_path_components(path, con)

out <- table_path_components(x, con)
out[[length(out)]]
vapply(components, FUN.VALUE = character(1), function(x) {
if (length(x) == 0) "" else x[[length(x)]]
})
}

#' @export
#' @rdname is_table_path
table_path_components <- function(x, con) {
UseMethod("table_path_components", con)
}

#' @export
table_path_components.DBIConnection <- function(x, con) {
quote_char <- substr(sql_escape_ident(con, ""), 1, 1)

scan(
text = x,
what = character(),
quote = quote_char,
quiet = TRUE,
na.strings = character(),
sep = "."
)
lapply(x, function(x) {
scan(
text = x,
what = character(),
quote = quote_char,
quiet = TRUE,
na.strings = character(),
sep = "."
)
})
}

#' @export
escape.dbplyr_table_path <- function(x, parens = FALSE, collapse = ", ", con = NULL) {
# names are always already escaped
alias <- names2(x)
table_path <- as_table_path(table_name(x, con), con)
table_path <- as_table_path(table_path_name(x, con), con)
has_alias <- alias == "" | alias == table_path

if (db_supports_table_alias_with_as(con)) {
Expand All @@ -116,14 +154,15 @@ check_table_id <- function(x, arg = caller_arg(x), call = caller_env()) {
}

is_table_id <- function(x) {
is_table_path(x) ||
is.ident(x) ||
is.ident(x) ||
methods::is(x, "Id") ||
is_catalog(x) ||
is_schema(x) ||
is.character(x)
((is.character(x) || is_table_path(x)) && length(x) == 1)
}

#' @export
#' @rdname is_table_path
check_table_path <- function(x,
error_arg = caller_arg(x),
error_call = caller_env()) {
Expand All @@ -136,6 +175,8 @@ check_table_path <- function(x,
}
}

#' @export
#' @rdname is_table_path
as_table_path <- function(x,
con,
error_arg = caller_arg(x),
Expand Down
13 changes: 10 additions & 3 deletions R/tbl-sql.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@
#' to avoid retrieving them from the database.
#' Mainly useful for better performance when creating
#' 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) {
#' @param check_from `r lifecycle::badge("deprecated")`
tbl_sql <- function(subclass,
src,
from,
...,
vars = NULL,
check_from = deprecated()) {
# Can't use check_dots_used(), #1429
check_character(vars, allow_null = TRUE)
if (lifecycle::is_present(check_from)) {
lifecycle::deprecate_warn("2.5.0", "tbl_sql(check_from)")
}

is_suspicious <- is_bare_string(from) && grepl(".", from, fixed = TRUE)
source <- as_table_source(from, con = src$con)
Expand All @@ -35,7 +43,6 @@ tbl_sql <- function(subclass, src, from, ..., vars = NULL, check_from = TRUE) {
}
)


dplyr::make_tbl(
c(subclass, "sql", "lazy"),
src = src,
Expand Down
Loading

0 comments on commit d287d45

Please sign in to comment.