我在一个名为function.sql:的文件中有以下内容
CREATE FUNCTION increment(i integer) RETURNS integer AS $$
BEGIN
RETURN i + 1;
END;
$$ LANGUAGE plpgsql;
我正在尝试使用Ant:在本地Postgres(9.3.4)数据库中执行它
<target name = "create" >
<sql driver="org.postgresql.Driver" url="${db.url}" userid="${db.username}" password="${db.password}">
<transaction src="create/functions.sql"/>
<classpath>
<pathelement location="lib/postgresql-9.3-1101.jdbc41.jar"/>
</classpath>
</sql>
</target>
(我在github上创建了一个示例项目。)
当我执行ant时,我得到错误:
BUILD FAILED
/home/paul/jobhop-workspace/AntSqlPostgresFunctions/build.xml:17: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$"
Position: 58
但是,如果我直接执行文件:
psql -d <mydb> functions.sql
它创建的函数很好。我已经尝试了这里和这里建议的解决方案,听起来像是同一个问题,但它们对我不起作用。
错误消息是因为Ant的属性扩展行为看到$$
,并在它到达PostgreSQL之前用单个$
替换它(请参阅http://ant.apache.org/manual/properties.html,部分$$扩展)我们可以用几种方法来解决这个问题。按照Ant手册中的建议,将美元字符加倍是可行的,但代价是在将文件传递给psql
客户端时使其不再工作。我使用$BODY$
或($whatever-you-fancy$
)而不是$$
来定义函数体,或者在build.xml中将SQL任务的expandproperties
参数设置为false,取得了更大的成功
完成后,我们遇到了第二个问题:Ant任务解析您的文件以查找它提供给JDBC的语句。然而,它不是很聪明:它只是在";"上拆分角色,即使他们在身体中间的函数定义(请参阅http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/SQLExec.java?view=markup#l724),那么我们将看到一个关于未终止字符串(即函数体的前半部分)的错误。
我们可以通过使用不同的分隔符并通过delimiter
和delimitertype
参数告诉SQL任务来绕过它。您项目的以下修补程序让它为我工作:
diff --git a/build.xml b/build.xml
index 7d8a990..5dbbdc5 100644
--- a/build.xml
+++ b/build.xml
@@ -14,7 +14,7 @@
</target>
<target name = "create" >
- <sql driver="org.postgresql.Driver" url="${db.url}"
userid="${db.username}" password="${db.password}">
+ <sql driver="org.postgresql.Driver" url="${db.url}"
userid="${db.username}" password="${db.password}" delimiter="/* END_STATEMENT */" delimitertype="row" >
<transaction src="create/functions.sql"/>
diff --git a/create/functions.sql b/create/functions.sql
index 3238d3e..1a8518f 100644
--- a/create/functions.sql
+++ b/create/functions.sql
@@ -1,9 +1,9 @@
-CREATE FUNCTION increment(i integer) RETURNS integer AS $$
+CREATE FUNCTION increment(i integer) RETURNS integer AS $BODY$
BEGIN
RETURN i + 1;
END;
-$$ LANGUAGE plpgsql;
-
+$BODY$ LANGUAGE plpgsql;
+/* END_STATEMENT */
我最初将语句分隔符放在-- END_STATEMENT
形式的注释中。这只会给人一种工作的错觉,因为在解析SQL时,Ant任务会在开始寻找分隔符之前丢弃这些注释。找不到分隔符,它只是将文件的全部内容作为一条语句传递给PostgreSQL,这在您的简单情况下有效,但随着函数列表的变长,这将不理想。
相反,我使用了C风格的注释,这些注释可以被PostgreSQL识别,但(目前)不能被Ant任务的解析器识别。当然,如果Ant任务的解析器发生更改,这可能会停止工作。
如果functions.sql
文件不需要从psql
开始工作,那么您可以考虑使用任何您喜欢的分隔符。它甚至不必是有效的SQL语法,因为Ant任务在将语句传递给PostgreSQL之前将其删除。