我正在构建一些实用程序函数来简化cast(statement as type)
从 R 中更轻松地编写 SQL 查询的过程。
我这样做的方式是通过一个主力函数,as_type
该函数由几个一次性函数(例如as_bigint
)调用;至关重要的是,我还认为as_type
直接调用是一个有效的用例。
代码的基本结构如下:
as_type = function(x, type) {
if (is.na(match(type, known_types())))
stop("Attempt to cast to unknown type ", type)
sprintf('cast(%s as %s)', deparse(substitute(x, parent.frame())), type)
}
as_bigint = function(x) as_type(x, 'bigint')
known_types = function() 'bigint'
# more complicated than this but for works the purposes of this Q
query_encode = glue::glue
具有预期的用途,例如
query_encode("select {as_bigint('1')}")
query_encode("select {as_type('1', 'bigint')}")
as_
(实际上,对于其他有效的 SQL 类型,还有几个有效的类型和函数;只有query_encode
被导出)
不幸的是,直接调用as_type
失败,因为如?substitute
(h/t Brodie G on Twitter)中所述:
如果 [a component of the parse tree] 不是 [the second argument to
substitute
]中的绑定符号env
,则它不变
query_encode("select {as_bigint('1')}")
# select cast("1" as bigint)
query_encode("select {as_type('1', 'bigint')}")
# select cast(x as bigint)
我已经制定了以下解决方法,但它几乎感觉不到强大:
as_type = function(x, type) {
if (is.na(match(type, known_types())))
stop("Attempt to cast to unknown Presto type ", type)
prev_call = as.character(tail(sys.calls(), 2L)[[1L]][[1L]])
valid_parent_re = sprintf('^as_(?:%s)$', paste(known_type(), collapse = '|'))
eval_env =
if (grepl(valid_parent_re, prev_call)) parent.frame() else environment()
sprintf(
'cast(%s as %s)',
gsub('"', "'", deparse(substitute(x, eval_env)), fixed = TRUE),
type
)
}
即,检查sys.calls()
并检查是否as_type
从其中一个as_
函数调用;将env
参数设置substitute
为parent.frame()
好像是,如果不是,则为当前环境。
这暂时有效:
query_encode("select {as_bigint('1')}")
# select cast("1" as bigint)
query_encode("select {as_type('1', 'bigint')}")
# select cast("1" as bigint)
问题是,这是解决这个问题的最佳方式吗?这样说来,这感觉像是一个基于意见的问题,但我的意思是——(1)这种方法是否像乍一看那样脆弱,(2)假设是这样,还有什么更强大的替代方案?
例如,值得注意的is.name(x)
是FALSE
from as_type
,但我不清楚如何使用它来继续。