読者です 読者をやめる 読者になる 読者になる

メモ:dplyrがバックエンドにSQLを投げるときの流れ

R dplyr

ちゃんと追ってないけど、print()するにせよcollect()するにせよ結局collect.tbl_sql()が呼ばれるっぽい。

その実装はこう。dbSendQuery()とかdbFetchが実質的なやりとりをする。

collect.tbl_sql <- function(x, ..., n = 1e5, warn_incomplete = TRUE) {
  assert_that(length(n) == 1, n > 0L)
  if (n == Inf) {
    n <- -1
  }

  sql <- sql_render(x)
  res <- dbSendQuery(x$src$con, sql)
  on.exit(dbClearResult(res))

  out <- dbFetch(res, n)
  if (warn_incomplete) {
    res_warn_incomplete(res, "n = Inf")
  }

  grouped_df(out, groups(x))
}

sql_renderはgeneric functionで、tbl_sql()の場合はsql_render.tbl_sqlが呼ばれる。

なので、独自の処理をしたければ、S3のクラスとして独自のクラスをtbl_sqlより先において、sql_render.独自のクラス()というメソッドをつくればこっちがdispatchされる。なのでこのsqlにはたぶん文字列だけじゃなくて任意のオブジェクトを含めることができる。

問題は次の行。DBIはS4なので結構縛りが厳しい。dbSendQuery()が受け付ける引数の型も固定されてそう、と思って調べると、案の定。characterしか受け付けない。

setMethod(
  "dbSendStatement", signature("DBIConnection", "character"),
  function(conn, statement, ...) {
    dbSendQuery(conn, statement, ...)
  }
)

(https://github.com/rstats-db/DBI/blob/bc730b94ec5349308c1070c370e9414fdc77cfd4/R/DBConnection.R#L154-L159)

つまり、dbSendQuery()に任意のパラメータを指定したい場合はcollect()の処理自体を変える必要があるのでcollect.独自クラス()をつくる必要がある。

指定するのが文字列だけでいいならdbSendQuery()の中でごにょごにょやれば大丈夫。たぶん。なので、SQLへの変換部分さえがんばることができれば↓これはdplyrでうまく動く可能性がある。