@@ -288,28 +288,44 @@ check_attributes <- function(attributes, call = caller_env()) {
288
288
}
289
289
290
290
# apple + spark drive config (#651) --------------------------------------------
291
- configure_spark <- function (call = caller_env()) {
291
+ # Method will attempt to:
292
+ # 1. Locate an installation of unixodbc / error out otherwise.
293
+ # 2. Verify the driver_config argument. Expect this to be a list with
294
+ # two fields:
295
+ # * path Vector of viable driver paths ( only first one is used )
296
+ # * url A location where the user can downlaod the driver from.
297
+ # See spark_simba_config, for example. Its return value is used as
298
+ # the value for this argument.
299
+ # 3. Inspect the config for some settings that can impact how our package
300
+ # performs.
301
+ # 4. If action == "modify" then we attempt to modify the config in-situ.
302
+ # 5. Otherwise we throw a warning asking the user to revise.
303
+ configure_simba <- function (driver_config ,
304
+ action = " modify" , call = caller_env()) {
292
305
if (! is_macos()) {
293
306
return (invisible ())
294
307
}
308
+ if (! is.null(getOption(" odbc.no_config_override" ))) {
309
+ return (invisible ())
310
+ }
295
311
296
312
unixodbc_install <- locate_install_unixodbc()
297
313
if (length(unixodbc_install ) == 0 ) {
298
314
error_install_unixodbc(call )
299
315
}
300
316
301
- spark_config <- locate_config_spark()
302
- if (length(spark_config ) == 0 ) {
303
- abort(
304
- c(
305
- " Unable to locate the needed spark ODBC driver." ,
306
- i = " Please install the needed driver from https://www.databricks.com/spark/odbc-drivers-download."
307
- ),
317
+ simba_config <- driver_config $ path
318
+ if (length(simba_config ) == 0 ) {
319
+ func <- cli :: warn
320
+ if (action == " modify" ) {
321
+ fun <- cli :: abort
322
+ }
323
+ func(
324
+ c(i = " Please install the needed driver from {driver_config$url}" ),
308
325
call = call
309
326
)
310
327
}
311
-
312
- configure_unixodbc_spark(unixodbc_install [1 ], spark_config [1 ], call )
328
+ configure_unixodbc_simba(unixodbc_install [1 ], simba_config [1 ], action , call )
313
329
}
314
330
315
331
locate_install_unixodbc <- function () {
@@ -369,51 +385,51 @@ error_install_unixodbc <- function(call) {
369
385
)
370
386
}
371
387
372
- # p. 44 https://downloads.datastax.com/odbc/2.6.5.1005/Simba%20Spark%20ODBC%20Install%20and%20Configuration%20Guide.pdf
373
- locate_config_spark <- function () {
374
- spark_env <- Sys.getenv(" SIMBASPARKINI" )
375
- if (! identical(spark_env , " " )) {
376
- return (spark_env )
377
- }
378
-
379
- common_dirs <- c(
380
- " /Library/simba/spark/lib" ,
381
- " /etc" ,
382
- getwd(),
383
- Sys.getenv(" HOME" )
384
- )
388
+ configure_unixodbc_simba <- function (unixodbc_install , simba_config , action , call ) {
385
389
386
- list.files(
387
- common_dirs ,
388
- pattern = " simba\\ .sparkodbc\\ .ini$" ,
389
- full.names = TRUE
390
- )
391
- }
392
-
393
- configure_unixodbc_spark <- function (unixodbc_install , spark_config , call ) {
394
390
# As shipped, the simba spark ini has an incomplete final line
395
391
suppressWarnings(
396
- spark_lines <- readLines(spark_config )
392
+ simba_lines <- readLines(simba_config )
397
393
)
398
-
399
- spark_lines_new <- replace_or_append(
400
- lines = spark_lines ,
401
- pattern = " ^ODBCInstLib= " ,
394
+ res <- replace_or_append(
395
+ lines = simba_lines ,
396
+ key_pattern = " ^ODBCInstLib= " ,
397
+ accepted_value = unixodbc_install ,
402
398
replacement = paste0(" ODBCInstLib=" , unixodbc_install )
403
399
)
404
-
405
- spark_lines_new <- replace_or_append(
406
- lines = spark_lines_new ,
407
- pattern = " ^DriverManagerEncoding=" ,
400
+ warnings <- character ()
401
+ if (action != " modify" && res $ modified ) {
402
+ warnings <- c(warnings , c(" *" = " Please consider revising the {.arg ODBCInstLib}
403
+ field in {.file {simba_config}} and setting its value to {.val {unixodbc_install}}" ))
404
+ }
405
+ simba_lines_new <- res $ new_lines
406
+ res <- replace_or_append(
407
+ lines = simba_lines_new ,
408
+ key_pattern = " ^DriverManagerEncoding=" ,
409
+ accepted_value = " UTF-16|utf-16" ,
408
410
replacement = " DriverManagerEncoding=UTF-16"
409
411
)
412
+ if (action != " modify" && res $ modified ) {
413
+ warnings <- c(warnings , c(" *" = " Please consider revising the
414
+ {.arg DriverManagerEncoding} field in {.file {simba_config}} and setting its
415
+ value to {.val UTF-16}." ))
416
+ }
417
+ if (length(warnings )) {
418
+ cli :: cli_warn(c(
419
+ c(i = " Detected potentially unsafe driver settings:" ),
420
+ warnings
421
+ ))
422
+ }
423
+ simba_lines_new <- res $ new_lines
410
424
411
- write_spark_lines(spark_lines , spark_lines_new , spark_config , call )
425
+ if (action == " modify" ) {
426
+ write_simba_lines(simba_lines , simba_lines_new , simba_config , call )
427
+ }
412
428
413
429
invisible ()
414
430
}
415
431
416
- write_spark_lines <- function (spark_lines , spark_lines_new , spark_config , call ) {
432
+ write_simba_lines <- function (spark_lines , spark_lines_new , spark_config , call ) {
417
433
if (identical(spark_lines , spark_lines_new )) {
418
434
return (invisible ())
419
435
}
@@ -436,23 +452,52 @@ write_spark_lines <- function(spark_lines, spark_lines_new, spark_config, call)
436
452
writeLines(spark_lines_new , spark_config )
437
453
}
438
454
455
+ # Interpret the argument as an `ODBC` driver
456
+ # and attempt to infer the directory housing it.
457
+ # It will return an empty character vector if unable to.
458
+ driver_dir <- function (driver ) {
459
+ # driver argument could be an outright path, or a name
460
+ # of a driver specified in odbcinst.ini Try to discern
461
+ driver_spec <- subset(odbcListDrivers(), name == driver )
462
+ if (nrow(driver_spec )) {
463
+ driver_path <- subset(driver_spec , attribute == " Driver" )$ value
464
+ } else {
465
+ driver_path <- driver
466
+ }
467
+
468
+ ret <- dirname(driver_path )
469
+ if (ret == " ." ) {
470
+ ret <- character ()
471
+ }
472
+ return (ret )
473
+ }
474
+
439
475
is_writeable <- function (path ) {
440
476
tryCatch(file.access(path , mode = 2 )[[1 ]] == 0 , error = function (e ) FALSE )
441
477
}
442
478
443
- # given a vector of lines in an ini file, look for a given key pattern.
444
- # the `replacement` is the whole intended line, giving the "key=value" pair.
445
- # if the key is found, replace that line with `replacement`.
446
- # if the key isn't found, append a new line with `replacement`.
447
- replace_or_append <- function (lines , pattern , replacement ) {
448
- matching_lines_loc <- grepl(pattern , lines )
479
+ # Given a vector of lines in an ini file, look for a given key pattern.
480
+ # If found:
481
+ # - No action if the `accepted_value` argument is found on line.
482
+ # - Replace otherwise.
483
+ # If not found: append.
484
+ # The `replacement` is the whole intended line, giving the desired
485
+ # "key=value" pair.
486
+ # @return a list with two elements:
487
+ # - new_lines: Potentially modified lines
488
+ # - modified: Whether method modified lines, where modified means
489
+ # both changed or appended.
490
+ replace_or_append <- function (lines , key_pattern , accepted_value , replacement ) {
491
+ matching_lines_loc <- grepl(key_pattern , lines )
449
492
matching_lines <- lines [matching_lines_loc ]
493
+ found_ok <- length(matching_lines ) != 0 &&
494
+ any(grepl(accepted_value , lines [matching_lines_loc ]))
450
495
if (length(matching_lines ) == 0 ) {
451
496
lines <- c(lines , replacement )
452
- } else {
497
+ } else if ( ! found_ok ) {
453
498
lines [matching_lines_loc ] <- replacement
454
499
}
455
- lines
500
+ return ( list ( new_lines = lines , modified = ! found_ok ))
456
501
}
457
502
458
503
check_shiny_session <- function (x ,
0 commit comments