diff --git a/DESCRIPTION b/DESCRIPTION index a5d3248..aa97025 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,12 +28,11 @@ Suggests: dplyr, knitr, rmarkdown, - RMySQL, RSQLite, shiny, testthat (>= 3.0.0), tibble -VignetteBuilder: +VignetteBuilder: knitr Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 5ae7e4e..c073568 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ export(Pool) export(dbBreak) export(dbPool) +export(demoDb) export(localCheckout) export(onActivate) export(onDestroy) diff --git a/NEWS.md b/NEWS.md index 3f5bb5b..d1d5ece 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,18 +1,20 @@ # pool (development version) +* Switched from hosted MySQL database to local SQLite database. + # pool 1.0.3 -* Now explicitly requires DBI 1.2.0 (#178) and messages if you're using an +* Now explicitly requires DBI 1.2.0 (#178) and messages if you're using an old dbplyr (#179). # pool 1.0.2 * No longer depends on the withr package, by instead requiring R 3.6. -* Add wrappers for dbplyr generics `db_col_types()` (#171) and +* Add wrappers for dbplyr generics `db_col_types()` (#171) and `db_copy_to()` (#172). -* Pool no longer generates spurious messages about needing to use +* Pool no longer generates spurious messages about needing to use `in_schema()` or avoiding the use of `ident_q()`. * Add support for new DBI generics that return Arrow objects. @@ -21,7 +23,7 @@ * `copy_to()` now returns a tbl that uses the Pool. -* Added missing methods for `sql_join_suffix()` (#165) and +* Added missing methods for `sql_join_suffix()` (#165) and `sql_query_explain()` (#167). # pool 1.0.0 @@ -44,7 +46,7 @@ * pool now implements the dbplyr 2.0.0 interface, eliminating warnings when using pool with dplyr (#132). -* Pool errors and warnings have been reviewed with an eye to making them +* Pool errors and warnings have been reviewed with an eye to making them more immediately actionable (#145). * Objects are now validated once on first checkout to ensure that the @@ -58,7 +60,7 @@ * `dbPool()`'s `validateQuery` is now actually used (#153). -* DBI methods should dispatch correctly in more cases; in particular +* DBI methods should dispatch correctly in more cases; in particular `dbReadTable()` and friends will now work correctly when used with `DBI::Id()` (#120). diff --git a/R/DBI.R b/R/DBI.R index 543bdc6..12cb941 100644 --- a/R/DBI.R +++ b/R/DBI.R @@ -20,29 +20,13 @@ #' @export #' @examples #' # You use a dbPool in the same way as a standard DBI connection -#' pool <- dbPool(RSQLite::SQLite()) +#' pool <- dbPool(RSQLite::SQLite(), dbname = demoDb()) #' pool #' -#' DBI::dbWriteTable(pool, "mtcars", mtcars) #' dbGetQuery(pool, "SELECT * FROM mtcars LIMIT 4") #' #' # Always close a pool when you're done using it #' poolClose(pool) -#' -#' # Using the RMySQL package -#' if (requireNamespace("RMySQL", quietly = TRUE)) { -#' pool <- dbPool( -#' drv = RMySQL::MySQL(), -#' dbname = "shinydemo", -#' host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", -#' username = "guest", -#' password = "guest" -#' ) -#' -#' dbGetQuery(pool, "SELECT * from City LIMIT 5;") -#' -#' poolClose(pool) -#' } dbPool <- function(drv, ..., minSize = 1, diff --git a/R/utils.R b/R/utils.R index 99a8c90..9bb74f8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -25,3 +25,27 @@ defer <- function(expr, envir = parent.frame(), after = FALSE) { thunk <- as.call(list(function() expr)) do.call(on.exit, list(thunk, TRUE, after), envir = envir) } + +#' Create a demo SQLite database +#' +#' This function creates a temporary SQLite database for demonstration purposes. +#' It populates the database with two tables: 'mtcars' and 'faithful'. +#' +#' @export +#' @keywords internal +demoDb <- function() { + check_installed("RSQLite") + + path <- file.path(tools::R_user_dir("pool", "cache"), "demo.sqlite3") + dir.create(dirname(path), showWarnings = FALSE, recursive = TRUE) + + if (!file.exists(path)) { + con <- DBI::dbConnect(RSQLite::SQLite(), path) + on.exit(DBI::dbDisconnect(con)) + + DBI::dbWriteTable(con, "mtcars", datasets::mtcars, row.names = "model") + DBI::dbWriteTable(con, "faithful", datasets::faithful) + } + + path +} diff --git a/README.md b/README.md index d1055e9..dc8d2e4 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Learn more about why pool is needed in `vignette("why-pool")`. ## Usage -Here’s a simple example of using a pool within a Shiny app (feel free to try it yourself): +Here’s a simple example of using a pool within a Shiny app: ```r library(shiny) @@ -25,33 +25,27 @@ library(dplyr) library(pool) loadNamespace("dbplyr") -pool <- dbPool( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" -) +pool <- dbPool(RSQLite::SQLite(), dbname = demoDb()) onStop(function() { poolClose(pool) }) ui <- fluidPage( - textInput("ID", "Enter your ID:", "5"), + textInput("cyl", "Enter your number of cylinders:", "4"), tableOutput("tbl"), - numericInput("nrows", "How many cities to show?", 10), + numericInput("nrows", "How many cars to show?", 10), plotOutput("popPlot") ) server <- function(input, output, session) { - city <- tbl(pool, "City") + cars <- tbl(pool, "mtcars") output$tbl <- renderTable({ - city |> filter(ID == !!input$ID) |> collect() + cars |> filter(cyl == !!input$cyl) |> collect() }) output$popPlot <- renderPlot({ - df <- city |> head(input$nrows) |> collect() - pop <- df |> pull("Population", name = "Name") + df <- cars |> head(input$nrows) |> collect() + pop <- df |> pull("mpg", name = "model") barplot(pop) }) } diff --git a/man/dbPool.Rd b/man/dbPool.Rd index 7828ad7..3d900ce 100644 --- a/man/dbPool.Rd +++ b/man/dbPool.Rd @@ -50,27 +50,11 @@ to the database if needed. } \examples{ # You use a dbPool in the same way as a standard DBI connection -pool <- dbPool(RSQLite::SQLite()) +pool <- dbPool(RSQLite::SQLite(), dbname = demoDb()) pool -DBI::dbWriteTable(pool, "mtcars", mtcars) dbGetQuery(pool, "SELECT * FROM mtcars LIMIT 4") # Always close a pool when you're done using it poolClose(pool) - -# Using the RMySQL package -if (requireNamespace("RMySQL", quietly = TRUE)) { - pool <- dbPool( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" - ) - - dbGetQuery(pool, "SELECT * from City LIMIT 5;") - - poolClose(pool) -} } diff --git a/man/demoDb.Rd b/man/demoDb.Rd new file mode 100644 index 0000000..cc3bb48 --- /dev/null +++ b/man/demoDb.Rd @@ -0,0 +1,13 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{demoDb} +\alias{demoDb} +\title{Create a demo SQLite database} +\usage{ +demoDb() +} +\description{ +This function creates a temporary SQLite database for demonstration purposes. +It populates the database with two tables: 'mtcars' and 'faithful'. +} +\keyword{internal} diff --git a/vignettes/.gitignore b/vignettes/.gitignore index 097b241..9e2bd63 100644 --- a/vignettes/.gitignore +++ b/vignettes/.gitignore @@ -1,2 +1,4 @@ *.html *.R + +/.quarto/ diff --git a/vignettes/articles/advanced-pool.Rmd b/vignettes/articles/advanced-pool.Rmd index 7dde986..bff486e 100644 --- a/vignettes/articles/advanced-pool.Rmd +++ b/vignettes/articles/advanced-pool.Rmd @@ -1,7 +1,7 @@ --- title: "Advanced usage" desc: > - This article discusses how to customize your pool and how to handle + This article discusses how to customize your pool and how to handle transactions. --- @@ -32,17 +32,11 @@ First, let's get to know our Pool object: ```{r} library(pool) -pool <- dbPool( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" -) +pool <- dbPool(RSQLite::SQLite(), dbname = demoDb()) pool ``` -As you can see, printing gives you basic information about your pool. This can be useful to learn how many connections you have open (both free/idle and in use). +As you can see, printing gives you basic information about your pool. This can be useful to learn how many connections you have open (both free/idle and in use). But for this section, let's turn our attention to other parameters that you can pass to `dbPool()`: `minSize`, `maxSize`, and `idleTimeout`. @@ -59,11 +53,8 @@ library(DBI) library(pool) pool <- dbPool( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest", + RSQLite::SQLite(), + dbname = demoDb(), minSize = 10, idleTimeout = 60 * 60 ) @@ -73,7 +64,7 @@ poolClose(pool) ## Transactions -So far, we've recommended you always use the pool object directly when you need to query the database. There's one challenge where this is not possible: transactions. Because for a transaction, you need to have access to the same connection for longer than a single query. The following will not necessary work because the pool might give you a different connection for each +So far, we've recommended you always use the pool object directly when you need to query the database. There's one challenge where this is not possible: transactions. Because for a transaction, you need to have access to the same connection for longer than a single query. The following will not necessary work because the pool might give you a different connection for each ```{r} #| eval: false diff --git a/vignettes/why-pool.Rmd b/vignettes/why-pool.Rmd index 133c2c7..96ad8af 100644 --- a/vignettes/why-pool.Rmd +++ b/vignettes/why-pool.Rmd @@ -34,39 +34,31 @@ The first extreme is have one connection per app: library(shiny) library(DBI) -# In a multi-file app, you could create conn at the top of your +# In a multi-file app, you could create conn at the top of your # server.R file or in global.R -conn <- DBI::dbConnect( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" -) +conn <- DBI::dbConnect(RSQLite::SQLite(), dbname = pool::demoDb()) onStop(function() { DBI::dbDisconnect(conn) }) ui <- fluidPage( - textInput("ID", "Enter your ID:", "5"), + textInput("cyl", "Enter your number of cylinders:", "4"), tableOutput("tbl"), - numericInput("nrows", "How many cities to show?", 10), + numericInput("nrows", "How many cars to show?", 10), plotOutput("popPlot") ) server <- function(input, output, session) { output$tbl <- renderTable({ - sql <- "SELECT * FROM City WHERE ID = ?id;" - query <- sqlInterpolate(conn, sql, id = input$ID) + sql <- "SELECT * FROM mtcars WHERE cyl = ?cyl;" + query <- sqlInterpolate(conn, sql, cyl = input$cyl) dbGetQuery(conn, query) }) output$popPlot <- renderPlot({ - sql <- "SELECT * FROM City LIMIT ?id;" - query <- sqlInterpolate(conn, sql, id = input$nrows) + sql <- "SELECT * FROM mtcars LIMIT ?n;" + query <- sqlInterpolate(conn, sql, n = input$nrows) df <- dbGetQuery(conn, query) - pop <- df$Population - names(pop) <- df$Name - barplot(pop) + barplot(setNames(df$mpg, df$model)) }) } @@ -89,19 +81,13 @@ library(shiny) library(DBI) connect <- function() { - DBI::dbConnect( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" - ) + DBI::dbConnect(RSQLite::SQLite(), dbname = pool::demoDb()) } ui <- fluidPage( - textInput("ID", "Enter your ID:", "5"), + textInput("cyl", "Enter your number of cylinders:", "4"), tableOutput("tbl"), - numericInput("nrows", "How many cities to show?", 10), + numericInput("nrows", "How many cars to show?", 10), plotOutput("popPlot") ) @@ -110,8 +96,8 @@ server <- function(input, output, session) { conn <- connect() on.exit(DBI::dbDisconnect(conn)) - sql <- "SELECT * FROM City WHERE ID = ?id;" - query <- sqlInterpolate(conn, sql, id = input$ID) + sql <- "SELECT * FROM mtcars WHERE cyl = ?cyl;" + query <- sqlInterpolate(conn, sql, cyl = input$cyl) dbGetQuery(conn, query) }) @@ -119,12 +105,10 @@ server <- function(input, output, session) { conn <- connect() on.exit(DBI::dbDisconnect(conn)) - sql <- "SELECT * FROM City LIMIT ?id;" - query <- sqlInterpolate(conn, sql, id = input$nrows) + sql <- "SELECT * FROM mtcars LIMIT ?n;" + query <- sqlInterpolate(conn, sql, n = input$nrows) df <- dbGetQuery(conn, query) - pop <- df$Population - names(pop) <- df$Name - barplot(pop) + barplot(setNames(df$mpg, df$model)) }) } @@ -158,37 +142,27 @@ The code is just as simple as the connection per app approach: all you need to d library(shiny) library(DBI) -pool <- pool::dbPool( - drv = RMySQL::MySQL(), - dbname = "shinydemo", - host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com", - username = "guest", - password = "guest" -) +pool <- pool::dbPool(RSQLite::SQLite(), dbname = pool::demoDb()) onStop(function() { pool::poolClose(pool) }) ui <- fluidPage( - textInput("ID", "Enter your ID:", "5"), + textInput("cyl", "Enter your number of cylinders:", "4"), tableOutput("tbl"), - numericInput("nrows", "How many cities to show?", 10), + numericInput("nrows", "How many cars to show?", 10), plotOutput("popPlot") ) server <- function(input, output, session) { + cars <- tbl(pool, "mtcars") + output$tbl <- renderTable({ - sql <- "SELECT * FROM City WHERE ID = ?id;" - query <- sqlInterpolate(pool, sql, id = input$ID) - dbGetQuery(pool, query) + cars |> filter(cyl == !!input$cyl) |> collect() }) - output$popPlot <- renderPlot({ - sql <- "SELECT * FROM City LIMIT ?id;" - query <- sqlInterpolate(conn, sql, id = input$nrows) - df <- dbGetQuery(pool, query) - pop <- df$Population - names(pop) <- df$Name + df <- cars |> head(input$nrows) |> collect() + pop <- df |> pull("mpg", name = "model") barplot(pop) }) }