我有一个宏:mac1(&rest args)
接受任意数量的参数
由于我不能将apply
与宏一起使用,并且由于我无法控制宏的实现,如何创建给定列表的函数fun1 (lst)
,使用扩展列表调用宏?
更新:我虽然问题已经足够,但似乎更好地陈述我的真实案例:
后现代提供了查询宏:
macro: query (query &rest args/format)
例如,您可以这样称呼它:
(query
"select * from example where col1 = $1::integer and col2 = $2::date"
123
"2017-01-01"
:str-alists)
好的,现在想象一下,在运行时我传递了这个列表:
'((:query "select * from table1 where c1 = $1 and c2 = $2 and c3 = $3"
:params (1 "2017-01-01" 3) :return :str-alists
(:query "select * from tab2 where c3 = $1"
:params ("somevalue") :return :lists)
.
.
.
))
我必须进行查询。我需要定义一个函数,它将这样的列表作为参数并进行查询。但由于查询是一个宏我有以下问题:我需要在某处评估列表,以便将它们传递给宏。目前我这样做(想法来自@melpomene评论):
(defun macro-apply(q p ret)
(eval (macroexpand `(query ,q ,@p ,ret))))
因此功能变为:
(defun exec-queries (lst)
(mapc
(lambda(x) (macro-apply
(getf x :query)
(getf x :params)
(getf x :return))
lst))
有没有更好的方法,或者我缺少哪些明显的东西?
答案 0 :(得分:3)
关于您的实际问题,请注意宏使用低级别包 cl-postgres 来使用函数实现查询(请参阅http://quickdocs.org/postmodern/api#system-cl-postgres)。宏只是底层API的语法糖。让我们看看如何使用参数化查询扩展宏:
CL-USER> (macroexpand
'(postmodern:query
"select * from table where col1=$1:integer"
20))
(PROGN
(CL-POSTGRES:PREPARE-QUERY POSTMODERN:*DATABASE* ""
"select * from table where col1=$1:integer")
(CL-POSTGRES:EXEC-PREPARED POSTMODERN:*DATABASE* "" (LIST 20)
'CL-POSTGRES:LIST-ROW-READER))
与SQL一样,您首先定义了准备好的语句,以避免注入攻击。如果您经常重复使用相同的查询,那么仅将它们准备一次会更有效(请参阅defprepared
)。然后,执行查询。
当您还指定结果类型(查询中的最后一个关键字参数)时,宏会调用未导出的reader-for-format
函数,以了解每行使用哪个回调函数。
(macroexpand
'(postmodern:query "select * from table where col1=$1:integer"
20
:str-alist))
(MULTIPLE-VALUE-CALL
#'(LAMBDA
(&OPTIONAL (POSTMODERN::ROWS) (POSTMODERN::AFFECTED) &REST #:G843)
(DECLARE (IGNORE #:G843))
(IF POSTMODERN::AFFECTED
(VALUES (CAR POSTMODERN::ROWS) POSTMODERN::AFFECTED)
(CAR POSTMODERN::ROWS)))
(PROGN
(CL-POSTGRES:PREPARE-QUERY POSTMODERN:*DATABASE* ""
"select * from table where col1=$1:integer")
(CL-POSTGRES:EXEC-PREPARED POSTMODERN:*DATABASE* "" (LIST 20)
'CL-POSTGRES:ALIST-ROW-READER)))
T
由于宏已经为您完成了所有工作,您可以有效地将查询包装在新表单中并对其进行评估,前提是您不评估任意Lisp表单(准备好的语句可以防止SQL注入,但EVAL打开另一个攻击向量;检查你的输入。)
或者,尝试了解如何使用查询的构建块来生成自己的查询解释器。乍一看,它似乎是某种内在的平台效应"获得任意查询并处理它们,但也许你有一个很好的用例。
答案 1 :(得分:2)
也可以使用COMPILE甚至COMPILE-FILE + LOAD来处理它。
例如,在某些情况下,以某种格式输出查询表单并将某些格式嵌入到文件中可能很有用。然后,您可以编译该文件并加载代码 - 要么在加载时执行,要么稍后执行。
原始版本:
IN-PACKAGE
表单放入其中并将一个或多个查询表单转储到其中。COMPILE-FILE
如果你想对结果做些什么,你必须在文件中加入更多的代码......
此示例要求函数compile-file
和必要的库/宏代码在运行时可用。