# db_schema_list() -------------------------------------------------------
#' List schemas on a database
#'
#' @description
#' `r lifecycle::badge("experimental")`
#'
#' `db_schema_list()` lists the available schemas on the database.
#'
#' @param con An object of class `"src"` or `"DBIConnection"`.
#' @param include_default Boolean, if `TRUE` (default), also the default schema
#' on the database is included in the result
#' @param ... Passed on to the individual methods.
#'
#' @details Methods are not available for all DBMS.
#'
#' Additional arguments are:
#'
#' - `dbname`: supported for MSSQL. List schemas on a different database on the connected MSSQL-server;
#' default: database addressed by `con`.
#'
#' @return A tibble with the following columns:
#' \describe{
#' \item{`schema_name`}{the names of the schemas,}
#' \item{`schema_owner`}{the schema owner names.}
#' }
#'
#' @export
#' @family schema handling functions
#'
#' @name db_schema_list
db_schema_list <- function(con, include_default = TRUE, ...) {
check_param_class(include_default, "logical")
# If we check in the method, we need to specify the user_env argument
if (inherits(con, "src_dbi")) {
deprecate_soft("0.2.5", 'dm::db_schema_list(con = "must be a DBI connection, not a dbplyr source,")', )
}
UseMethod("db_schema_list")
}
#' @export
db_schema_list.src_dbi <- function(con, include_default = TRUE, ...) {
db_schema_list(con$con, include_default = include_default, ...)
}
#' @export
`db_schema_list.Microsoft SQL Server` <- function(con, include_default = TRUE, dbname = NULL, ...) {
dbname_sql <- if (is_null(dbname)) {
""
} else {
check_param_class(dbname, "character")
check_param_length(dbname)
paste0(DBI::dbQuoteIdentifier(con, dbname), ".")
}
default_if_true <- if_else(include_default, "", " AND NOT s.name = 'dbo'")
# ignore built-in schemas for backward compatibility:
# https://docs.microsoft.com/en-us/sql/relational-databases/security/authentication-access/ownership-and-user-schema-separation?view=sql-server-ver15
DBI::dbGetQuery(con, glue::glue("SELECT s.name as schema_name
FROM {dbname_sql}sys.schemas s
WHERE s.name NOT IN ('sys', 'guest', 'INFORMATION_SCHEMA', 'db_accessadmin',
'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin',
'db_denydatareader', 'db_denydatawriter', 'db_owner',
'db_securityadmin'){default_if_true}")) %>%
as_tibble()
}
#' @export
db_schema_list.PqConnection <- function(con, include_default = TRUE, ...) {
default_if_true <- if_else(include_default, "", ", 'public'")
DBI::dbGetQuery(con, glue::glue("SELECT schema_name, schema_owner FROM information_schema.schemata WHERE
schema_name NOT IN ('information_schema', 'pg_catalog'{default_if_true})
AND schema_name NOT LIKE 'pg_toast%'
AND schema_name NOT LIKE 'pg_temp_%'
ORDER BY schema_name")) %>%
as_tibble()
}
#' @export
db_schema_list.SQLiteConnection <- function(con, include_default = TRUE, ...) {
abort_no_schemas_supported("SQLite")
}
#' @export
db_schema_list.Pool <- function(con, include_default = TRUE, ...) {
pool_con <- pool::poolCheckout(con)
on.exit(pool::poolReturn(pool_con))
db_schema_list(pool_con, include_default, ...)
}
# db_schema_exists() -----------------------------------------------------
#' Check for existence of a schema on a database
#'
#' @description
#' `r lifecycle::badge("experimental")`
#'
#' `db_schema_exists()` checks, if a schema exists on the database.
#'
#' @inheritParams db_schema_list
#' @param schema Class `character` or `SQL`, name of the schema
#'
#' @details Methods are not available for all DBMS.
#'
#' Additional arguments are:
#'
#' - `dbname`: supported for MSSQL. Check if a schema exists on a different
#' database on the connected MSSQL-server; default: database addressed by `con`.
#' @return A boolean: `TRUE` if schema exists, `FALSE` otherwise.
#'
#' @family schema handling functions
#' @export
db_schema_exists <- function(con, schema, ...) {
check_param_class(schema, "character")
check_param_length(schema)
# If we check in the method, we need to specify the user_env argument
if (inherits(con, "src_dbi")) {
deprecate_soft("0.2.5", 'dm::db_schema_exists(con = "must be a DBI connection, not a dbplyr source,")', )
}
UseMethod("db_schema_exists")
}
#' @export
db_schema_exists.src_dbi <- function(con, schema, ...) {
db_schema_exists(con$con, schema, ...)
}
#' @export
`db_schema_exists.Microsoft SQL Server` <- function(con, schema, dbname = NULL, ...) {
sql_to_character(con, schema) %in% db_schema_list(con, dbname = dbname)$schema_name
}
#' @export
db_schema_exists.PqConnection <- function(con, schema, ...) {
sql_to_character(con, schema) %in% db_schema_list(con)$schema_name
}
#' @export
db_schema_exists.SQLiteConnection <- function(con, schema, ...) {
abort_no_schemas_supported("SQLite")
}
# db_schema_create() -----------------------------------------------------
#' Create a schema on a database
#'
#' @description
#' `r lifecycle::badge("experimental")`
#'
#' `db_schema_create()` creates a schema on the database.
#'
#' @inheritParams db_schema_list
#' @param schema Class `character` or `SQL` (cf. Details), name of the schema
#'
#' @details Methods are not available for all DBMS.
#'
#' An error is thrown if a schema of that name already exists.
#'
#' The argument `schema` (and `dbname` for MSSQL) can be provided as `SQL` objects.
#' Keep in mind, that in this case it is assumed that they are already correctly quoted as identifiers
#' using [DBI::dbQuoteIdentifier()].
#'
#' Additional arguments are:
#'
#' - `dbname`: supported for MSSQL. Create a schema in a different
#' database on the connected MSSQL-server; default: database addressed by `con`.
#'
#' @return `NULL` invisibly.
#'
#' @family schema handling functions
#' @export
db_schema_create <- function(con, schema, ...) {
check_param_class(schema, "character")
check_param_length(schema)
# If we check in the method, we need to specify the user_env argument
if (inherits(con, "src_dbi")) {
deprecate_soft("0.2.5", 'dm::db_schema_create(con = "must be a DBI connection, not a dbplyr source,")', )
}
UseMethod("db_schema_create")
}
#' @export
db_schema_create.src_dbi <- function(con, schema, ...) {
db_schema_create(con$con, schema, ...)
}
#' @export
db_schema_create.PqConnection <- function(con, schema, ...) {
DBI::dbExecute(con, glue("CREATE SCHEMA {DBI::dbQuoteIdentifier(con, schema)}"))
message(glue::glue("Schema {tick(sql_to_character(con, schema))} created."))
invisible(NULL)
}
#' @export
`db_schema_create.Microsoft SQL Server` <- function(con, schema, dbname = NULL, ...) {
if (!is_null(dbname)) {
original_dbname <- attributes(con)$info$dbname
DBI::dbExecute(con, glue::glue("USE {DBI::dbQuoteIdentifier(con, dbname)}"))
withr::defer(DBI::dbExecute(con, glue::glue("USE {DBI::dbQuoteIdentifier(con, original_dbname)}")))
}
msg_suffix <- fix_msg(sql_to_character(con, dbname))
DBI::dbExecute(con, glue("CREATE SCHEMA {DBI::dbQuoteIdentifier(con, schema)}"))
message(glue::glue("Schema {tick(sql_to_character(con, schema))} created{msg_suffix}."))
invisible(NULL)
}
#' @export
db_schema_create.SQLiteConnection <- function(con, schema, ...) {
abort_no_schemas_supported("SQLite")
}
# sql_schema_table_list() -------------------------------------------------
# List the tables in a schema on a database
#
# @description `sql_schema_table_list()` list the tables in a schema on the database.
#
# @inheritParams db_schema_exists
#
# @details Methods are not available for all DBMS.
#
# An error is thrown if no schema of that name exists.
#
# Additional arguments are:
#
# - `dbname`: supported for MSSQL. Look for tables on a different
# database on the connected MSSQL-server; default: database addressed by `con`.
#
# @return A tibble with the following columns:
# \describe{
# \item{`table_name`}{name of the table,}
# \item{`remote_name`}{identifier of the table on the DBMS.
# Can be used to access the listed tables with the syntax
# `tbl(con, remote_name).`}
# }
#
# @family schema handling functions
# @export
# sql_schema_table_list <- function(con, schema = NULL, ...) {
# if (!is_null(schema)) {
# check_param_class(schema, "character")
# check_param_length(schema)
# }
# if (!is_null(schema) && !db_schema_exists(con, schema, ...)) {
# abort_no_schema_exists(sql_to_character(con_from_src_or_con(con), schema), ...)
# }
# UseMethod("sql_schema_table_list")
# }
# FIXME: this should be done using a dplyr function
sql_schema_table_list_mssql <- function(con, schema = NULL, dbname = NULL) {
src <- src_from_src_or_con(con)
if (!is_null(schema)) {
check_param_class(schema, "character")
check_param_length(schema)
}
if (!is_null(dbname)) {
check_param_class(dbname, "character")
check_param_length(dbname)
}
enframe(
get_src_tbl_names(src, schema = sql_to_character(src$con, schema), dbname = dbname),
name = "table_name",
value = "remote_name"
)
}
# FIXME: this should be done using a dplyr function
sql_schema_table_list_postgres <- function(con, schema = NULL) {
src <- src_from_src_or_con(con)
if (!is_null(schema)) {
check_param_class(schema, "character")
check_param_length(schema)
}
enframe(
get_src_tbl_names(src, schema = sql_to_character(src$con, schema)),
name = "table_name",
value = "remote_name"
)
}
# db_schema_drop() -------------------------------------------------------
#' Remove a schema from a database
#'
#' @description
#' `r lifecycle::badge("experimental")`
#'
#' `db_schema_drop()` deletes a schema from the database.
#' For certain DBMS it is possible to force the removal of a non-empty schema, see below.
#'
#' @inheritParams db_schema_create
#' @param force Boolean, default `FALSE`. Set to `TRUE` to drop a schema and
#' all objects it contains at once. Currently only supported for Postgres/Redshift.
#'
#' @details Methods are not available for all DBMS.
#'
#' An error is thrown if no schema of that name exists.
#'
#' The argument `schema` (and `dbname` for MSSQL) can be provided as `SQL` objects.
#' Keep in mind, that in this case it is assumed that they are already correctly quoted as identifiers.
#'
#' Additional arguments are:
#'
#' - `dbname`: supported for MSSQL. Remove a schema from a different
#' database on the connected MSSQL-server; default: database addressed by `con`.
#'
#' @return `NULL` invisibly.
#'
#' @family schema handling functions
#' @export
db_schema_drop <- function(con, schema, force = FALSE, ...) {
check_param_class(schema, "character")
check_param_length(schema)
check_param_class(force, "logical")
check_param_length(force)
# If we check in the method, we need to specify the user_env argument
if (inherits(con, "src_dbi")) {
deprecate_soft("0.2.5", 'dm::db_schema_drop(con = "must be a DBI connection, not a dbplyr source,")', )
}
UseMethod("db_schema_drop")
}
#' @export
db_schema_drop.src_dbi <- function(con, schema, force = FALSE, ...) {
db_schema_drop(con$con, schema, force, ...)
}
#' @export
db_schema_drop.PqConnection <- function(con, schema, force = FALSE, ...) {
if (force) {
force_infix <- " and all objects it contained"
force_suffix <- " CASCADE"
} else {
force_infix <- ""
force_suffix <- ""
}
DBI::dbExecute(
con,
glue("DROP SCHEMA {DBI::dbQuoteIdentifier(con, schema)}{force_suffix}")
)
message(glue::glue("Dropped schema {tick(sql_to_character(con, schema))}{force_infix}."))
invisible(NULL)
}
#' @export
`db_schema_drop.Microsoft SQL Server` <- function(con, schema, force = FALSE, dbname = NULL, ...) {
warn_if_arg_not(
force,
only_on = c("Postgres", "Redshift"),
correct = FALSE,
additional_msg = "Please remove potential objects from the schema manually."
)
if (!is_null(dbname)) {
check_param_class(dbname, "character")
check_param_length(dbname)
original_dbname <- attributes(con)$info$dbname
DBI::dbExecute(con, glue("USE {dbname}"))
withr::defer(DBI::dbExecute(con, glue("USE {original_dbname}")))
}
msg_infix <- fix_msg(sql_to_character(con, dbname))
DBI::dbExecute(con, glue("DROP SCHEMA {DBI::dbQuoteIdentifier(con, schema)}"))
message(glue::glue("Dropped schema {tick(sql_to_character(con, schema))}{msg_infix}."))
invisible(NULL)
}
#' @export
db_schema_drop.SQLiteConnection <- function(con, schema, force = FALSE, ...) {
abort_no_schemas_supported("SQLite")
}
fix_msg <- function(dbname) {
if (!is_null(dbname)) {
msg_suffix <- paste0(" on database ", tick(dbname))
} else {
msg_suffix <- ""
}
}
sql_to_character <- function(con, sql_or_char) {
if (inherits(sql_or_char, "SQL")) {
sql_or_char <- map_chr(
sql_or_char,
~ DBI::dbUnquoteIdentifier(con, .x)[[1]] %>% pluck("name")
)
}
sql_or_char
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.