为什么在通过函数调用时 dbListTables 会给出警告消息? (R DBI)
Why does dbListTables give a warning message when called via a function? (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
在此先感谢您的帮助!
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)
}
}
顶级表达式被传递给函数并用于检查连接是否有效。另一个名为 on_connection_opened
的 odbc
函数会进行一些检查。如果这在任何地方引发错误,则会给出警告,因为 tryCatch
.
那么为什么函数 on_connection_opened
会崩溃?
该函数采用以下参数:
on_connection_opened <- function(connection, code)
它做的第一件事是:
display_name <- connection@info$dbname
这似乎与警告信息相符:
trying to get slot "info" from an object of a basic class ("character") with no slots
从参数名称可以明显看出,函数 on_connection_opened
在其第一个参数中需要一个数据库连接对象。它从调用者那里得到什么? eval(expr[[2]])
这是原始调用的左侧:con
在本例中,这是一个连接对象,一切都很好。
现在我们有足够的信息来回答您的问题:
为什么我会收到此警告消息?
您的函数创建了一个连接,该连接排队检查连接函数。如果然后检查表列表和 returns 那个。检查连接函数然后将表列表解释为好像它是一个连接,尝试检查它,但悲惨地失败了。这会引发警告。
更具体地说,为什么我只在通过函数调用 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 中连接选项卡中连接的显示。如果您再次在同一个函数中关闭连接,这看起来就没那么有趣了。所以这对你的功能来说不是必需的。
我使用 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
在此先感谢您的帮助!
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)
}
}
顶级表达式被传递给函数并用于检查连接是否有效。另一个名为 on_connection_opened
的 odbc
函数会进行一些检查。如果这在任何地方引发错误,则会给出警告,因为 tryCatch
.
那么为什么函数 on_connection_opened
会崩溃?
该函数采用以下参数:
on_connection_opened <- function(connection, code)
它做的第一件事是:
display_name <- connection@info$dbname
这似乎与警告信息相符:
trying to get slot "info" from an object of a basic class ("character") with no slots
从参数名称可以明显看出,函数 on_connection_opened
在其第一个参数中需要一个数据库连接对象。它从调用者那里得到什么? eval(expr[[2]])
这是原始调用的左侧:con
在本例中,这是一个连接对象,一切都很好。
现在我们有足够的信息来回答您的问题:
为什么我会收到此警告消息?
您的函数创建了一个连接,该连接排队检查连接函数。如果然后检查表列表和 returns 那个。检查连接函数然后将表列表解释为好像它是一个连接,尝试检查它,但悲惨地失败了。这会引发警告。
更具体地说,为什么我只在通过函数调用 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 中连接选项卡中连接的显示。如果您再次在同一个函数中关闭连接,这看起来就没那么有趣了。所以这对你的功能来说不是必需的。