Reputation: 1843
I have the following in a file called function.sql:
CREATE FUNCTION increment(i integer) RETURNS integer AS $$
BEGIN
RETURN i + 1;
END;
$$ LANGUAGE plpgsql;
And I'm trying to execute it against a local Postgres (9.3.4) database with Ant:
<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>
(I've created an example project on github here.)
When I execute ant, I get the error:
BUILD FAILED
/home/paul/jobhop-workspace/AntSqlPostgresFunctions/build.xml:17: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$"
Position: 58
However if I execute the file directly:
psql -d <mydb> functions.sql
it creates the function just fine. I've tried the solutions suggested here and here, which sound like the same problem, but they didn't work for me.
Upvotes: 1
Views: 1041
Reputation: 515
The error message is because Ant's property expansion behaviour sees the $$
and replaces it with a single $
before it gets to PostgreSQL (see http://ant.apache.org/manual/properties.html, section $$ Expansion)
We can get around that in a few ways. Doubling up the dollar characters as recommended in the Ant manual works, but at the expense of making the file no longer work when passed to the psql
client. I've had more success using $BODY$
or ($whatever-you-fancy$
) instead of $$
to delimit the function body, or by setting the SQL task's expandproperties
argument to false in the build.xml.
Once that's done we hit a second problem: the Ant task parses your file to find statements which it feeds to JDBC. However, it's not very smart: it just splits on ';' characters, even when they're in the middle of the body of your function definition (see http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/SQLExec.java?view=markup#l724), so then we'll see an error about an unterminated string (i.e. the first half of the function body).
We can get round that by using a different delimiter and telling the SQL task about it via the delimiter
and delimitertype
arguments. The following patch to your project
gets it working for me:
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 */
I initially put the statement delimiter in a comment of the form -- END_STATEMENT
. That only gives the illusion of working, since, when parsing the SQL, the Ant task discards those comments before it goes looking for delimiters. Finding no delimiter it just passes the entire contents of the file to PostgreSQL as a single statement, which works in your simple case but won't be ideal as the list of functions gets longer.
Instead, I used C-style comments which are recognized by PostgreSQL, but not (currently) by the Ant task's parser. Of course, this could stop working if the Ant task's parser is changed.
If the functions.sql
file doesn't need to work from psql
then you can consider using anything you like as delimiter. It doesn't even have to be valid SQL syntax since the Ant task removes it before passing on the statements to PostgreSQL.
Upvotes: 4