2017-10-13 60 views
4

我用DBI包中的dbListTables编写了一个函数,抛出一个我无法理解的警告。当我在函数之外运行相同的代码时,我没有收到警告消息。为什么dbListTables在通过函数调用时会给出警告消息? (R DBI)

有关信息,使用的数据库是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。)

警告消息

当执行上面的代码时,得到以下的警告MESSA当使用第一个选项时(通过函数),但当我使用第二个选项(没有功能)时我没有得到它。

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 

在此先感谢您的帮助!

+0

我使用'dbGetQuery'得到相同的警告,所以这似乎是一个更广泛的问题。 – JAD

回答

4

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

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函数调用on_connection_opened然后进行一些检查。如果这在任何地方抛出一个错误,由于tryCatch,给出警告。

那么为什么功能on_connection_opened会崩溃?

该函数采用下列参数:

on_connection_opened <- function(connection, code) 

和它做的第一件事是:

display_name <- [email protected]$dbname 

这似乎符合警告消息:

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

从参数的名称中可以明显看出函数on_connection_opened需要在其第一个参数中使用数据库连接对象。它从调用者那里得到什么? eval(expr[[2]])

这是原始呼叫的左手边:con

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

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

为什么会收到这样的警告消息?

你的函数创建一个连接,它将检查连接函数排队。如果然后检查表的列表并返回它。检查连接函数然后将表的列表解释为它是连接,尝试检查它并且失败。这会引发警告。

更具体地说,为什么我只在通过函数调用dbListTables时才得到它?

dbListTables不是罪魁祸首,dbConnect是。因为你是从函数内部调用它的,所以它没有得到它正在尝试检查并失败的连接对象。

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

解决方法是单独打开连接并将其传递到您的函数中。这样连接就会在自己的通话中打开,所以检查工作正常。

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

before <- getTaskCallbackNames() 
    con <- odbc::dbConnect(odbc::odbc(), ...) 
    after <- getTaskCallbackNames() 
    removeTaskCallback(which(!after %in% before)) 

是一个正在运行的on_connection_opened必不可少的?它究竟做了什么?

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

+0

感谢您解决这个问题!如果我理解正确,但这不是我的错误,对吗? 'addTaskCallBack'函数以某种方式使用'on_connection_opened'的错误表达式?如果这确实是正确的,那么你知道这个问题是否会在不久的将来得到解决? – Willem

+0

@Willem正确。我对包装的内部运作知之甚少,无法判断对该功能的调用是否必要。我可能会为它提交一个错误报告。但是目前,打开连接并将它传递给你的函数应该能够正确地处理所有事情。 – JAD

+1

@Willem https://github.com/rstats-db/odbc/issues/122 – JAD

相关问题