diff --git a/R/dbConnect_PqDriver.R b/R/dbConnect_PqDriver.R index b06b8c9c1..62f6b5ba5 100644 --- a/R/dbConnect_PqDriver.R +++ b/R/dbConnect_PqDriver.R @@ -36,7 +36,10 @@ #' for details on this file and syntax. #' @param ... Other name-value pairs that describe additional connection #' options as described at -#' +#' . +#' For example, set `options = "-c search_path=myschema"` to make +#' [dbListTables()] and unqualified reads and writes use `myschema` +#' by default for that connection. #' @param bigint The R type that 64-bit integer types should be mapped to, #' default is [bit64::integer64], which allows the full range of 64 bit #' integers. @@ -50,11 +53,34 @@ #' set to [Sys.timezone()] or `""`. #' This setting does not change the time values returned, only their display. #' @param conn Connection to disconnect. +#' @section Additional connection options: +#' Use `...` for libpq parameters that are not formal arguments to +#' `dbConnect()`. Besides SSL settings, this can be useful for session options +#' such as `search_path`: +#' +#' ``` +#' dbConnect( +#' RPostgres::Postgres(), +#' options = "-c search_path=analytics" +#' ) +#' ``` +#' #' @rdname Postgres #' @examplesIf postgresHasDefault() #' library(DBI) #' # Pass more arguments as necessary to dbConnect() #' con <- dbConnect(RPostgres::Postgres()) +#' +#' schema <- "rpostgres_example" +#' dbExecute(con, paste0("DROP SCHEMA IF EXISTS ", dbQuoteIdentifier(con, schema), " CASCADE")) +#' dbExecute(con, paste0("CREATE SCHEMA ", dbQuoteIdentifier(con, schema))) +#' dbWriteTable(con, Id(schema = schema, table = "schema_demo"), iris[1:3, ], overwrite = TRUE) +#' +#' con2 <- dbConnect(RPostgres::Postgres(), options = paste0("-c search_path=", schema)) +#' dbListTables(con2) +#' dbDisconnect(con2) +#' +#' dbExecute(con, paste0("DROP SCHEMA ", dbQuoteIdentifier(con, schema), " CASCADE")) #' dbDisconnect(con) #' @usage NULL dbConnect_PqDriver <- function( diff --git a/R/tables.R b/R/tables.R index eb2ca520b..cc6d24504 100644 --- a/R/tables.R +++ b/R/tables.R @@ -10,6 +10,10 @@ #' Pass an identifier created with [Id()] as the `name` argument #' to specify the schema or catalog, e.g. #' `name = Id(catalog = "my_catalog", schema = "my_schema", table = "my_table")` . +#' To list objects in a specific schema without changing the connection +#' defaults, use [dbListObjects()] with `Id(schema = "my_schema")`. +#' To make [dbListTables()] use a schema by default for a connection, set +#' `options = "-c search_path=my_schema"` in [dbConnect()]. #' To specify the tablespace, use #' `dbExecute(conn, "SET default_tablespace TO my_tablespace")` #' before creating the table. @@ -52,6 +56,19 @@ #' dbWriteTable(con, "mtcars2", mtcars[0, ], temporary = TRUE) #' dbReadTable(con, "mtcars2") #' +#' schema <- "rpostgres_example" +#' dbExecute(con, paste0("DROP SCHEMA IF EXISTS ", dbQuoteIdentifier(con, schema), " CASCADE")) +#' dbExecute(con, paste0("CREATE SCHEMA ", dbQuoteIdentifier(con, schema))) +#' dbWriteTable(con, Id(schema = schema, table = "schema_demo"), iris[1:3, ], overwrite = TRUE) +#' +#' dbListObjects(con, Id(schema = schema)) +#' +#' con2 <- dbConnect(RPostgres::Postgres(), options = paste0("-c search_path=", schema)) +#' dbListTables(con2) +#' dbDisconnect(con2) +#' +#' dbExecute(con, paste0("DROP SCHEMA ", dbQuoteIdentifier(con, schema), " CASCADE")) +#' #' dbDisconnect(con) #' @name postgres-tables NULL diff --git a/man/Postgres.Rd b/man/Postgres.Rd index 2e3e03e88..e58143979 100644 --- a/man/Postgres.Rd +++ b/man/Postgres.Rd @@ -56,7 +56,10 @@ for details on this file and syntax.} \item{...}{Other name-value pairs that describe additional connection options as described at -\url{https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS}} +\url{https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS}. +For example, set \code{options = "-c search_path=myschema"} to make +\code{\link[DBI:dbListTables]{dbListTables()}} and unqualified reads and writes use \code{myschema} +by default for that connection.} \item{bigint}{The R type that 64-bit integer types should be mapped to, default is \link[bit64:integer64]{bit64::integer64}, which allows the full range of 64 bit @@ -85,11 +88,35 @@ but still recommended; if you delete the object containing the connection, it will be automatically disconnected during the next GC with a warning. } +\section{Additional connection options}{ + +Use \code{...} for libpq parameters that are not formal arguments to +\code{dbConnect()}. Besides SSL settings, this can be useful for session options +such as \code{search_path}: + +\if{html}{\out{
}}\preformatted{dbConnect( + RPostgres::Postgres(), + options = "-c search_path=analytics" +) +}\if{html}{\out{
}} +} + \examples{ \dontshow{if (postgresHasDefault()) withAutoprint(\{ # examplesIf} library(DBI) # Pass more arguments as necessary to dbConnect() con <- dbConnect(RPostgres::Postgres()) + +schema <- "rpostgres_example" +dbExecute(con, paste0("DROP SCHEMA IF EXISTS ", dbQuoteIdentifier(con, schema), " CASCADE")) +dbExecute(con, paste0("CREATE SCHEMA ", dbQuoteIdentifier(con, schema))) +dbWriteTable(con, Id(schema = schema, table = "schema_demo"), iris[1:3, ], overwrite = TRUE) + +con2 <- dbConnect(RPostgres::Postgres(), options = paste0("-c search_path=", schema)) +dbListTables(con2) +dbDisconnect(con2) + +dbExecute(con, paste0("DROP SCHEMA ", dbQuoteIdentifier(con, schema), " CASCADE")) dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/Redshift.Rd b/man/Redshift.Rd index 99eae630d..82fe2caeb 100644 --- a/man/Redshift.Rd +++ b/man/Redshift.Rd @@ -53,7 +53,10 @@ for details on this file and syntax.} \item{...}{Other name-value pairs that describe additional connection options as described at -\url{https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS}} +\url{https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS}. +For example, set \code{options = "-c search_path=myschema"} to make +\code{\link[DBI:dbListTables]{dbListTables()}} and unqualified reads and writes use \code{myschema} +by default for that connection.} \item{bigint}{The R type that 64-bit integer types should be mapped to, default is \link[bit64:integer64]{bit64::integer64}, which allows the full range of 64 bit diff --git a/man/postgres-tables.Rd b/man/postgres-tables.Rd index e462e1aa9..5c97913c2 100644 --- a/man/postgres-tables.Rd +++ b/man/postgres-tables.Rd @@ -139,6 +139,10 @@ SQL string. Pass an identifier created with \code{\link[=Id]{Id()}} as the \code{name} argument to specify the schema or catalog, e.g. \code{name = Id(catalog = "my_catalog", schema = "my_schema", table = "my_table")} . +To list objects in a specific schema without changing the connection +defaults, use \code{\link[DBI:dbListObjects]{dbListObjects()}} with \code{Id(schema = "my_schema")}. +To make \code{\link[DBI:dbListTables]{dbListTables()}} use a schema by default for a connection, set +\code{options = "-c search_path=my_schema"} in \code{\link[DBI:dbConnect]{dbConnect()}}. To specify the tablespace, use \code{dbExecute(conn, "SET default_tablespace TO my_tablespace")} before creating the table. @@ -159,6 +163,19 @@ dbExistsTable(con, "mtcars") dbWriteTable(con, "mtcars2", mtcars[0, ], temporary = TRUE) dbReadTable(con, "mtcars2") +schema <- "rpostgres_example" +dbExecute(con, paste0("DROP SCHEMA IF EXISTS ", dbQuoteIdentifier(con, schema), " CASCADE")) +dbExecute(con, paste0("CREATE SCHEMA ", dbQuoteIdentifier(con, schema))) +dbWriteTable(con, Id(schema = schema, table = "schema_demo"), iris[1:3, ], overwrite = TRUE) + +dbListObjects(con, Id(schema = schema)) + +con2 <- dbConnect(RPostgres::Postgres(), options = paste0("-c search_path=", schema)) +dbListTables(con2) +dbDisconnect(con2) + +dbExecute(con, paste0("DROP SCHEMA ", dbQuoteIdentifier(con, schema), " CASCADE")) + dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/tests/testthat/test-dbConnect.R b/tests/testthat/test-dbConnect.R index f7b354a3e..f3f9115b4 100644 --- a/tests/testthat/test-dbConnect.R +++ b/tests/testthat/test-dbConnect.R @@ -54,6 +54,53 @@ test_that("passing other options parameters", { expect_identical(r$application_name, "apple") }) +test_that("search_path option exposes schema tables via dbListTables", { + con <- postgresDefault() + con2 <- NULL + schema <- paste0("rpostgres_docs_", as.integer(Sys.getpid())) + on.exit( + { + if (!is.null(con2) && dbIsValid(con2)) { + dbDisconnect(con2) + } + if (dbIsValid(con)) { + quoted_schema <- as.character(dbQuoteIdentifier(con, schema)) + try( + dbExecute( + con, + paste0("DROP SCHEMA IF EXISTS ", quoted_schema, " CASCADE") + ), + silent = TRUE + ) + dbDisconnect(con) + } + }, + add = TRUE + ) + + quoted_schema <- as.character(dbQuoteIdentifier(con, schema)) + dbExecute(con, paste0("DROP SCHEMA IF EXISTS ", quoted_schema, " CASCADE")) + dbExecute(con, paste0("CREATE SCHEMA ", quoted_schema)) + table_name <- "schema_demo" + dbWriteTable( + con, + Id(schema = schema, table = table_name), + iris[1:3, ], + overwrite = TRUE + ) + + schema_tables <- dbListObjects(con, Id(schema = schema)) + expect_equal(nrow(schema_tables), 1L) + expect_false(schema_tables$is_prefix[[1]]) + expect_equal( + schema_tables$table[[1]], + Id(schema = schema, table = table_name) + ) + + con2 <- postgresDefault(options = paste0("-c search_path=", schema)) + expect_equal(dbListTables(con2), table_name) +}) + test_that("error if passing unkown parameters", { skip_on_cran() expect_error(