Skip to content

Commit

Permalink
Improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveViss committed Oct 15, 2024
1 parent 73c6fcf commit 851eb5f
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 74 deletions.
11 changes: 8 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,23 @@ Imports:
config (>= 0.3.2),
DBI (>= 1.2.2),
dplyr (>= 1.1.4),
htmltools(>= 0.5.8.1),
glue (>= 1.7.0),
golem (>= 0.4.1),
htmltools (>= 0.5.8.1),
leaflet (>= 2.2.1),
leaflet.extras (>= 2.0.1),
mapedit (>= 0.0.6),
purrr (>= 1.0.2),
RSQLite (>= 2.3.5),
reactable (>= 0.4.4),
RSQLite (>= 2.3.5),
shiny (>= 1.8.0),
shinyWidgets (>= 0.8.6),
sf (>= 1.0.18),
stringr (>= 1.5.1),
tidyselect (>= 1.2.1)
rlang (>= 1.1.4),
tidyselect (>= 1.2.1),
tidyr (>= 1.3.1),
waiter (>= 0.2.5)
Suggests:
knitr,
readxl (>= 1.4.3),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export(detect_cens)
export(fct_start)
export(get_db)
export(get_db_cached)
export(get_tbl)
export(get_tbl_info)
export(get_tbl_notnulls)
export(get_tbl_pkeys)
Expand Down
18 changes: 15 additions & 3 deletions R/db_con.R
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#' Init sqlite connexion
#' Initialize the PLUMES database connection
#'
#' Connects to the PLUMES database file. If `local` is provided, it uses the specified file path,
#' otherwise it defaults to the internal database path and name.
#' The function ensures the database file exists before connecting.
#'
#' @param local Optional. Path to a local SQLite database file. Defaults to `NULL` for the internal path.
#'
#' @return A DBI connection object.
#'
#' @examples
#' \dontrun{
#' init_con()
#' }
#' # Connect to a local database
#' con <- init_con(local = "path/to/local/database.sqlite")
#'
#' # Connect using default settings
#' con <- init_con()
#' }
#'
#' @export
init_con <- function(local = NULL){
if(!is.null(local)){
Expand Down
147 changes: 104 additions & 43 deletions R/db_tbl.R
Original file line number Diff line number Diff line change
@@ -1,117 +1,178 @@
#' Add entry within specified table
#' Insert a new row into a table
#'
#' @param con connexion object returned by DBI::dbConnect()
#' @param tbl a character name of the table
#' @param ... a vector of column names in the specified table
#' Inserts a new row into a specified table in the database.
#' It ensures that fields, primary keys, and NOT NULL constraints are valid before inserting.
#'
#' @export
#' @param con DBI connection object.
#' @param tbl Name of the target table.
#' @param ... Named column-value pairs to insert into the table.
#'
#' @return None.
#'
insert_entry <- function(con = NULL, tbl = NULL, ...){
#' @examples
#' \dontrun{
#' con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
#' insert_entry(con, "my_table", id = 1, name = "Sample", value = 10)
#' }
#'
#' @export
insert_entry <- function(con = NULL, tbl = NULL, ...) {
fields <- list(...)

check_fields_exist(con, tbl, names(fields))
check_fields_pkeys(con, tbl, names(fields))
check_fields_notnulls(con, tbl, names(fields))

ddl <- glue::glue_sql("INSERT INTO { tbl } ({ names(fields)* }) VALUES ({ fields* });", .con = con)
res <- DBI::dbSendStatement(con, ddl)
res <- DBI::dbSendStatement(con, ddl)

on.exit(DBI::dbClearResult(res))
}

#' Add entry within specified table
#' Insert multiple rows into a table
#'
#' @param con connexion object returned by DBI::dbConnect()
#' @param tbl a character name of the table
#' @param data a data.frame
#' Performs bulk insertion into a table using transactions.
#' Rolls back if any errors occur.
#'
#' @export
#' @param con DBI connection object.
#' @param tbl Name of the target table.
#' @param data A data.frame with rows to insert.
#'
#' @return None.
#'
#' @examples
#' \dontrun{
#' con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
#' data <- data.frame(id = 1:3, name = c("A", "B", "C"), value = c(10, 20, 30))
#' bulk_insert_entry(con, "my_table", data)
#' }
#'
bulk_insert_entry <- function(con = NULL, tbl = NULL, data = NULL){
#' @export
bulk_insert_entry <- function(con = NULL, tbl = NULL, data = NULL) {
tryCatch({
RSQLite::dbBegin(con, name = "bulk_insert")
purrr::pmap(data, ~with(list(...),{
insert_entry(con = con, tbl = tbl,...)
purrr::pmap(data, ~with(list(...), {
insert_entry(con = con, tbl = tbl, ...)
}))
RSQLite::dbCommit(con, name = "bulk_insert")
}, error = \(e){
}, error = \(e) {
RSQLite::dbRollback(con, name = "bulk_insert")
cli::cli_abort(e)
})
}

#' @describeIn insert_entry Modify entry within specified table.
#' @export
#' Modify a row in a table
#'
#' Updates an existing row based on primary keys. Only one row is updated.
#'
#' @param con DBI connection object.
#' @param tbl Name of the target table.
#' @param ... Named column-value pairs for the row to update.
#'
#' @return None.
#'
modify_entry <- function(con = NULL, tbl = NULL, ...){
#' @examples
#' \dontrun{
#' con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
#' modify_entry(con, "my_table", id = 1, name = "Updated", value = 20)
#' }
#'
#' @export
modify_entry <- function(con = NULL, tbl = NULL, ...) {
fields <- list(...)

check_fields_exist(con, tbl, names(fields))
check_fields_pkeys(con, tbl, names(fields))

pkeys_tbl <- get_tbl_pkeys(con, tbl)
target_row <- do.call("search_tbl", list(con = con, tbl = tbl) |> append(fields[pkeys_tbl]))

if(nrow(target_row) > 1L){
if (nrow(target_row) > 1L) {
cli::cli_abort("More than one row found with { fields[pkeys_tbl] }")
} else {

pkeys_values <- fields[which(names(fields) %in% pkeys_tbl)]
update_values <- fields[-which(names(fields) %in% pkeys_tbl)]

update_entries <- purrr::map(names(update_values), \(n){
update_entries <- purrr::map(names(update_values), \(n) {
glue::glue("{n} = ${n}")
}) |> glue::glue_sql_collapse(",")

criterias <- purrr::map(names(pkeys_values), \(n){
criterias <- purrr::map(names(pkeys_values), \(n) {
glue::glue("{n} = ${n}")
}) |> glue::glue_sql_collapse(" AND ")

ddl <- glue::glue_sql("
UPDATE { tbl }
SET { update_entries }
WHERE { criterias };
", .con = con)

res <- DBI::dbSendStatement(con, ddl)
DBI::dbBind(res, fields)

on.exit(DBI::dbClearResult(res))
}
}

#' @describeIn insert_entry Delete entry within specified table.
#' @export
#' Delete a row from a table
#'
#' Deletes a row from a table based on primary keys.
#'
#' @param con DBI connection object.
#' @param tbl Name of the target table.
#' @param ... Named primary key column-value pairs for the row to delete.
#'
#' @return None.
#'
delete_entry <- function(con = NULL, tbl = NULL, ...){
#' @examples
#' \dontrun{
#' con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
#' delete_entry(con, "my_table", id = 1)
#' }
#'
#' @export
delete_entry <- function(con = NULL, tbl = NULL, ...) {
fields <- list(...)

check_fields_exist(con, tbl, names(fields))
check_fields_pkeys(con, tbl, names(fields))

pkeys_tbl <- get_tbl_pkeys(con, tbl)
target_row <- do.call("search_tbl", list(con = con, tbl = tbl) |> append(fields[pkeys_tbl]))

if(nrow(target_row) > 1L){
target_row <- do.call("search_tbl", list(con = con, tbl = tbl) |> append(fields[pkeys_tbl]))
if (nrow(target_row) > 1L) {
cli::cli_abort("More than one row found with { fields[pkeys_tbl] }")
} else {
criterias <- purrr::map(names(fields[pkeys_tbl]), \(n){
criterias <- purrr::map(names(fields[pkeys_tbl]), \(n) {
glue::glue("{n} = ${n}")
}) |> glue::glue_sql_collapse(" AND ")

ddl <- glue::glue_sql("
DELETE
FROM { tbl }
WHERE { criterias };
", .con = con)

res <- DBI::dbSendStatement(con, ddl)
DBI::dbBind(res, fields)

on.exit(DBI::dbClearResult(res))
}
}


#' Retrieve data from a specified table in the database
#'
#' This function reads and returns all the rows from a specified table in the database.
#' It uses [DBI::dbReadTable()] to extract the contents of the table and return them as a data frame.
#'
#' @param con A DBI connection object returned by [DBI::dbConnect()]. This connection is used to interact with the database.
#' @param tbl A character string specifying the name of the target table in the database.
#'
#' @return A data frame containing all rows and columns from the specified table.
#' If the table is empty or does not exist, an error or empty data frame will be returned.
#'
#' @export
get_tbl <- function(con = NULL, tbl = NULL) {
DBI::dbReadTable(con, tbl)
}
22 changes: 17 additions & 5 deletions man/bulk_insert_entry.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions man/delete_entry.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions man/get_tbl.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 851eb5f

Please sign in to comment.