Skip to content

Commit

Permalink
add helper to locate configuration files (#680)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpcouch authored Dec 19, 2023
1 parent 8528e33 commit 6a8e295
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 3 deletions.
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Collate:
'driver-sqlite.R'
'driver-teradata.R'
'driver-vertica.R'
'odbc-config.R'
'odbc-data-sources.R'
'odbc-data-type.R'
'odbc-drivers.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export(odbcConnectionColumns)
export(odbcConnectionIcon)
export(odbcDataType)
export(odbcListColumns)
export(odbcListConfig)
export(odbcListDataSources)
export(odbcListDrivers)
export(odbcListObjectTypes)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# odbc (development version)

* Added a function `odbcListConfig()` to help locate configuration files on
macOS and Linux (@simonpcouch, #565).

* Use correct parent class for Oracle (#685).


# odbc 1.4.0

## Major changes
Expand Down
57 changes: 57 additions & 0 deletions R/odbc-config.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#' List locations of ODBC configuration files
#'
#' @description
#' On MacOS and Linux, odbc uses the unixODBC driver manager to manage
#' information about driver and data sources. This helper returns the filepaths
#' where the driver manager will look for that information.
#'
#' This function is a wrapper around the command line call `odbcinst -j`.
#'
#' Windows does not use `.ini` configuration files; this function will return a
#' 0-length vector on Windows.
#'
#' @seealso
#' The [odbcListDrivers()] and [odbcListDataSources()] helpers return
#' information on the contents of `odbcinst.ini` and `odbc.ini` files,
#' respectively.
#'
#' Learn more about unixODBC and the `odbcinst` utility
#' [here](https://www.unixodbc.org/odbcinst.html).
#'
#' @examplesIf FALSE
#' configs <- odbcListConfig()
#'
#' file.edit(configs[1])
#' @export
odbcListConfig <- function() {
if (is_windows()) {
return(character(0))
}

if (!has_odbc()) {
abort(
c("The unixODBC driver manager is not available. ",
"Please install and try again.")
)
}

res <- system("odbcinst -j", intern = TRUE)
res <- res[grepl("\\.ini", res)]
res <- strsplit(res, "\\:")

if (!identical(vapply(res, length, numeric(1)), c(2, 2, 2))) {
abort("Failed to parse output from odbcinst.", .internal = TRUE)
}

res <- vapply(res, `[[`, character(1), 2)
res <- trimws(res)
names(res) <- c("drivers", "system_dsn", "user_dsn")

res
}

system <- NULL

has_odbc <- function() {
!identical(unname(Sys.which("odbcinst")), "")
}
4 changes: 4 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ escapePattern <- function(x, charsToEsc = c("_"), escChar = "\\\\") {
x <- gsub(pattern, replace, x)
I(x)
}

is_windows <- function() {
identical(.Platform$OS.type, "windows")
}
4 changes: 1 addition & 3 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
}

.onLoad <- function(libname, pkgname) {
is_windows <- identical(.Platform$OS.type, "windows")

# If TZDIR is not set we need to set it to R's timezone database on windows,
# we can use the standard timezone database elsewhere.
if (is_windows) {
if (is_windows()) {
if (!nzchar(Sys.getenv("TZDIR"))) Sys.setenv("TZDIR" = file.path(R.home(), "share", "zoneinfo"))
}
}
Expand Down
Binary file modified inst/diagrams.key
Binary file not shown.
Binary file modified man/figures/r-interface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions man/odbcListConfig.Rd

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

29 changes: 29 additions & 0 deletions tests/testthat/_snaps/odbc-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# odbcListConfig errors informatively without unixODBC

Code
odbcListConfig()
Condition
Error in `odbcListConfig()`:
! The unixODBC driver manager is not available.
* Please install and try again.

# odbcListConfig errors informatively with unexpected odbcinst output

Code
odbcListConfig()
Condition
Error in `odbcListConfig()`:
! Failed to parse output from odbcinst.
i This is an internal error that was detected in the odbc package.
Please report it at <https://github.com/r-dbi/odbc/issues> with a reprex (<https://tidyverse.org/help/>) and the full backtrace.

---

Code
odbcListConfig()
Condition
Error in `odbcListConfig()`:
! Failed to parse output from odbcinst.
i This is an internal error that was detected in the odbc package.
Please report it at <https://github.com/r-dbi/odbc/issues> with a reprex (<https://tidyverse.org/help/>) and the full backtrace.

37 changes: 37 additions & 0 deletions tests/testthat/test-odbc-config.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
test_that("odbcListConfig returns appropriate result", {
skip_on_os(c("windows", "solaris"))
skip_if(!has_odbc(), "odbcinst not available.")

res <- odbcListConfig()

expect_type(res, "character")
expect_length(res, 3)
expect_named(res, c("drivers", "system_dsn", "user_dsn"))
expect_match(res, "\\.ini")
})

test_that("odbcListConfig returns an empty vector on Windows", {
local_mocked_bindings(is_windows = function() {TRUE})

res <- odbcListConfig()

expect_equal(res, character(0))
})

test_that("odbcListConfig errors informatively without unixODBC", {
local_mocked_bindings(is_windows = function() {FALSE},
has_odbc = function() {FALSE})

expect_snapshot(error = TRUE, odbcListConfig())
})

test_that("odbcListConfig errors informatively with unexpected odbcinst output", {
local_mocked_bindings(is_windows = function() {FALSE},
has_odbc = function() {TRUE})

local_mocked_bindings(system = function(...) {c("beep", "bop")})
expect_snapshot(error = TRUE, odbcListConfig())

local_mocked_bindings(system = function(...) {""})
expect_snapshot(error = TRUE, odbcListConfig())
})

0 comments on commit 6a8e295

Please sign in to comment.