Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add helper to locate configuration files #680

Merged
merged 13 commits into from
Dec 19, 2023
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.

8 changes: 0 additions & 8 deletions tests/testthat/_snaps/SQLServer.md

This file was deleted.

File renamed without changes.
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())
})
Loading