От новичка до гуру: Курсы программирования на CyberDuff

Почему dbListTables выдает предупреждающее сообщение при вызове через функцию? (Р ДБИ)

Я написал функцию, используя dbListTables из пакета 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.)

Предупреждающее сообщение

При выполнении приведенного выше кода я получаю следующее предупреждающее сообщение при использовании первого варианта (через функцию), но не получаю его при использовании второго варианта (без функции).

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  

Заранее благодарю за любую помощь!

13.10.2017

  • Я получаю то же предупреждение, используя dbGetQuery, так что это кажется более широкой проблемой. 18.10.2017

Ответы:


1

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 <- connection@info$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 необходимым? Что именно он делает?

Как объяснил создатель пакета в этом комментарии на Github, функция обрабатывает отображение соединения на вкладке соединений в RStudio. На это не так интересно смотреть, если вы снова закроете соединение в той же функции. Так что это не обязательно для вашей функции.

18.10.2017
  • Спасибо, что разобрались с этим! Если я правильно понимаю, это не ошибка с моей стороны, верно? Функция addTaskCallBack каким-то образом использует неправильное выражение для on_connection_opened? Если это действительно так, как вы думаете, будет ли это исправлено в ближайшем будущем? 18.10.2017
  • @Виллем Верно. Я недостаточно знаю внутреннюю работу пакета, чтобы судить о том, необходим ли вызов этой функции или нет. Я мог бы подать отчет об ошибке для него. Но на данный момент открытие соединения и передача его вашей функции должны делать все правильно. 18.10.2017
  • Новые материалы

    Интерфейс не является наследованием
    Долгое время меня путал принцип подстановки Лисков , потому что я не понимал контекста, к которому относился принцип. Будучи относительно новым программистом, я не знал разницы между интерфейсом..

    Стратегии прогнозирования временных рядов в ETNA
    Здравствуйте, меня зовут Дмитрий, и я один из разработчиков ETNA, пакета для прогнозирования временных рядов. В этой статье я объясню, что такое стратегии прогнозирования и как их использовать..

    Что такое Webpack?
    Webpack - это не что иное, как сборщик статических модулей, который позволяет отображать ваш код в веб-браузере. Он рекурсивно строит граф зависимостей, который включает каждый модуль, необходимый..

    Всегда ли данные говорят правду?
    Наша еженедельная подборка "Выбор редакции" и оригинальные материалы, которые необходимо прочитать. Объем данных, которые мы можем собрать, за последние годы вырос в геометрической прогрессии,..

    Обратное распространение в RNN объяснил
    Пошаговое объяснение вычислительных графов и обратного распространения ошибки в рекуррентной нейронной сети. Введение На заре машинного обучения, когда не было фреймворков, большая часть..

    Используйте Python для анализа рейтингов и обзоров курсов OMSCS Технологического института Джорджии
    Как получать и анализировать рейтинги курсов OMSCS, данные о сложности и рабочей нагрузке и компилировать их в интерактивную информационную панель Программа Онлайн-магистр компьютерных наук..

    Стеки и очереди в Python
    Стеки и очереди в Python Изучите 2 популярные линейные структуры данных. Зачем это читать? Стеки и очереди ( произносится как kyo͞o или kiu ) - это простые, но мощные структуры данных,..