为什么dbListTables在通过函数调用时会发出警告消息? (R DBI)

时间:2017-10-13 11:06:00

标签: sql-server r odbc r-dbi

我使用DBI包中的dbListTables编写了一个函数,它抛出了一个我无法理解的警告。当我在一个函数之外运行相同的代码时,我没有收到警告信息。

有关信息,使用的数据库是Microsoft SQL Server。

可重复的示例

library(odbc)
library(DBI)

# dbListTables in a function: gives a warning message

dbListTablesTest <- function(dsn, userName, password){

  con <- dbConnect(
    odbc::odbc(),
    dsn      = dsn,
    UID      = userName,
    PWD      = password,
    Port     = 1433,
    encoding = "latin1"
  )

  availableTables <- dbListTables(con)
}

availableTables <- 
  dbListTablesTest(
    dsn = "myDsn"
    ,userName = myLogin
    ,password = myPassword
  )

# dbListTables not within a function works fine (no warnings)

con2 <- dbConnect(
  odbc::odbc(),
  dsn      = "myDsn",
  UID      = myLogin,
  PWD      = myPassword,
  Port     = 1433,
  encoding = "latin1"
)

availableTables <- dbListTables(con2)

(顺便说一下,我意识到我应该使用dbDisconnect在使用它之后关闭连接。但这似乎会引发类似的警告。所以为了简单起见,我省略了dbDisconnect。)

警告消息

执行上面的代码时,在使用第一个选项(通过函数)时会收到以下警告消息,但是在使用第二个选项(无函数)时我没有得到它。

warning messages from top-level task callback '1'
Warning message:
Could not notify connection observer. trying to get slot "info" from an object of a basic class ("character") with no slots 

警告显然是由dbListTables引起的,因为当我从上面的函数中省略该行时它会消失。

我的问题

  • 为什么我收到此警告信息?
  • 更具体地说,为什么我只在通过函数调用dbListTables时才得到它?
  • 我做错了什么/我应该做些什么来避免它?

我的会话信息

R version 3.4.2 (2017-09-28)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=Dutch_Belgium.1252  LC_CTYPE=Dutch_Belgium.1252    LC_MONETARY=Dutch_Belgium.1252 LC_NUMERIC=C                   LC_TIME=Dutch_Belgium.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  tools     methods   base     

other attached packages:
[1] DBI_0.7    odbc_1.1.3

loaded via a namespace (and not attached):
[1] bit_1.1-12     compiler_3.4.2 hms_0.3        tibble_1.3.4   Rcpp_0.12.13   bit64_0.9-7    blob_1.1.0     rlang_0.1.2  

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:4)

TL:在另一个函数中调用odbc::dbConnect的DR会导致此警告。

经过大量挖掘odbc github后,我找到了警告的来源。调用dbConnect会创建数据库连接。在此函数中有以下代码:

# perform the connection notification at the top level, to ensure that it's had
# a chance to get its external pointer connected, and so we can capture the
# expression that created it
if (!is.null(getOption("connectionObserver"))) { # nocov start
  addTaskCallback(function(expr, ...) {
    tryCatch({
      if (is.call(expr) && identical(expr[[1]], as.symbol("<-"))) {
        # notify if this is an assignment we can replay
        on_connection_opened(eval(expr[[2]]), paste(
          c("library(odbc)", deparse(expr)), collapse = "\n"))
      }
    }, error = function(e) {
      warning("Could not notify connection observer. ", e$message, call. = FALSE)
    })

    # always return false so the task callback is run at most once
    FALSE
  })
} # nocov end

warning电话应该看起来很熟悉。这就是产生警告的原因。那么为什么会这样呢?

上面的代码片段试图对连接对象进行一些检查,看看是否一切顺利。

如何做到这一点,是通过添加一个函数来检查'TaskCallBack'。这是在top-level task完成后执行的函数列表。我对此并不是100%肯定,但据我所知,这意味着这些函数在调用堆栈中的最高函数完成后执行。

通常,这将是您脚本中的一行。例如:

library(odbc)
con <- odbc::dbConnect(odbc::odbc(), ...)

第二行中的赋值完成后,执行以下函数:

function(expr, ...) {
    tryCatch({
      if (is.call(expr) && identical(expr[[1]], as.symbol("<-"))) {
        # notify if this is an assignment we can replay
        on_connection_opened(eval(expr[[2]]), paste(
          c("library(odbc)", deparse(expr)), collapse = "\n"))
      }
    }, error = function(e) {
      warning("Could not notify connection observer. ", e$message, call. = FALSE)
    }
}

顶级表达式传递给函数并用于检查连接是否有效。另一个名为odbc的{​​{1}}函数会进行一些检查。如果这在任何地方引发错误,则会发出警告,因为on_connection_opened

那么为什么函数tryCatch会崩溃?

该函数采用以下参数:

on_connection_opened

它的第一件事就是:

on_connection_opened <- function(connection, code)

这似乎与警告信息相符:

  

尝试从没有插槽的基本类(“字符”)的对象获取插槽“info”

从参数的名称可以看出,函数display_name <- connection@info$dbname 在第一个参数中需要一个数据库连接对象。它从来电者那里得到了什么? on_connection_opened

这是原始通话的左侧:eval(expr[[2]])

在这种情况下,这是一个连接对象,一切都很好。

现在我们有足够的信息来回答您的问题:

为什么我收到此警告消息?

您的函数会创建一个连接,该连接将检查连接功能排队。如果然后检查表的列表并返回该表。检查连接函数然后解释表的列表,就好像它是一个连接,尝试检查它,并且悲惨地失败。这会引发警告。

更具体地说,为什么我只通过函数调用dbListTables来获取它?

con不是罪魁祸首,dbListTables是。因为你是在一个函数中调用它,所以它不会获得它试图检查并失败的连接对象。

我做错了什么/我该怎么做才能避免它?

解决方法是单独打开连接并将其传递给您的函数。这样连接就会在自己的调用中打开,因此检查工作正常。

或者,您可以再次删除TaskCallback:

dbConnect

运行是 before <- getTaskCallbackNames() con <- odbc::dbConnect(odbc::odbc(), ...) after <- getTaskCallbackNames() removeTaskCallback(which(!after %in% before)) 必不可少的?它究竟做了什么?

正如软件包在this comment on Github中的创建者所解释的那样,该函数处理RStudio中连接选项卡中连接的显示。如果再次关闭同一功能中的连接,这就不那么有趣了。所以这对你的功能来说并不重要。