ERROR: unterminated quoted string at or near
Asked Answered
E

9

47

While executing below shown trigger code using ANT I am getting the error:

org.postgresql.util.PSQLException: ERROR: unterminated quoted string at or near "' DECLARE timeout integer" Position: 57

I am able to sucessfully execute the below code through PGADmin (Provided by postgres) and command line utility "psql" and the trigger function is added but while executing through ANT it fails everytime

BEGIN TRANSACTION;

CREATE OR REPLACE FUNCTION sweeper() RETURNS trigger as '
    DECLARE
    timeout integer;
    BEGIN
    timeout = 30 * 24 * 60 * 60 ;
        DELETE FROM diagnosticdata WHERE current_timestamp - teststarttime  > (timeout * ''1 sec''::interval);
        return NEW;
    END;
' LANGUAGE 'plpgsql';

-- Trigger: sweep on diagnosticdata

CREATE TRIGGER sweep
  AFTER INSERT
  ON diagnosticdata
  FOR EACH ROW
  EXECUTE PROCEDURE sweeper();
END;
Eddy answered 17/8, 2010 at 5:6 Comment(0)
H
60

I encountered this error in liquibase and this page was one of the first search results so I guess I share my solution at this page:

You can put your whole sql in a separate file and include this in the changeset. Its important to set the splitStatements option to false.

The whole changeset would then look like

<changeSet author="fgrosse" id="530b61fec3ac9">
    <sqlFile path="your_sql_file_here.sql" splitStatements="false"/>
</changeSet>

I always like to have those big SQL parts (like function updates and such) in separate files. This way you get proper syntax highlighting when opening the sql file and dont have to intermix XML and SQL in one file.


Edit: as mentioned in the comments its worth noting that the sql change supports the splitStatements option as well (thx to AndreyT for pointing that out).

Hanfurd answered 24/2, 2014 at 15:33 Comment(1)
I think need to mention, what sql supports splitStatements="false" too. docMaynor
C
31

I had the same problem with the JDBC driver used by Liquibase.

It seems that the driver explodes each line ended by a semicolon and runs it as a separate SQL command. That is why the code below will be executed by the JDBC driver in the following sequence:

  1. CREATE OR REPLACE FUNCTION test(text) RETURNS VOID AS ' DECLARE tmp text
  2. BEGIN tmp := "test"
  3. END;
  4. ' LANGUAGE plpgsql

Of course, this is invalid SQL and causes the following error:

unterminated dollar-quoted string at or near ' DECLARE tmp text

To correct this, you need to use backslashes after each line ended with semicolon:

CREATE OR REPLACE FUNCTION test(text) 
RETURNS void AS ' DECLARE tmp text; \
BEGIN 
tmp := "test"; \
END;' LANGUAGE plpgsql;

Alternatively, you can place the whole definition in one line.

Catacaustic answered 27/12, 2010 at 20:28 Comment(3)
If using a LiquiBase formatted SQL file try adding this comment to your proc/function file splitStatements:false. That did the trick for me.Jubal
hi, thanks for this answer. it helped me with a similar error. however, i wanted to add that your work-around (adding the backslashes) only works in some cases. the splitting into separate statements is done by a a higher level that jdbc, so exactly what is taken as a line and what not depends on other software in the stack. in my case, using spring, the backslash does not work, but it was possible to specify a different separator.Morrison
@andrewcooke Please, how do you specify a different separator to solve the issue?Unpopular
C
11

I am using HeidiSQL client and this was solved by placing DELIMITER // before CREATE OR REPLACE statement. There is a also a 'Send batch in one go' option in HeidiSQL that essentially achieves the same thing.

Cleanup answered 15/10, 2014 at 8:22 Comment(3)
Send batch in one go helps in HeidiSQLNiveous
Lifesaver, thank you - I would never have found this on my own. The option is in the dropdown menu on the Execute SQL button.Freeload
I was facing this issue in HeidiSQL, Then tried with DBViewer, It worked without any change.Celebrate
V
2

This error arises as an interaction between the particular client used to connect to the server and the form of the function. To illustrate:

The following code will run without casualty in Netbeans 7, Squirrel, DbSchema, PgAdmin3

CREATE OR REPLACE FUNCTION author.revision_number()
  RETURNS trigger AS
$BODY$
 begin
  new.rev := new.rev + 1;
  new.revised := current_timestamp;
  return new;
 end;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

Please note that the 'begin' statement comes immediately after the '$' quoted string.

The next code will halt all the above clients except PgAdmin3.

CREATE OR REPLACE FUNCTION author.word_count()
  RETURNS trigger AS 
$BODY$
   declare
    wordcount integer := 0; -- counter for words
    indexer integer := 1;  -- position in the whole string
    charac char(1);  -- the first character of the word
    prevcharac char(1);
   begin

    while indexer <= length(new.blab) loop
      charac := substring(new.blab,indexer,1); -- first character of string

      if indexer = 1 then
        prevcharac := ' '; -- absolute start of counting
      else
        prevcharac := substring(new.blab, indexer - 1, 1); -- indexer has increased
      end if;

     if prevcharac = ' ' and charac != ' ' then
       wordcount := wordcount + 1;
     end if;

     indexer := indexer + 1;
   end loop;
  new.words := wordcount;
  return new;
  end;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

The crucial difference in the second example is the 'declare' section. The ploy of using back-slashes raises an error with PgAdmin3.

In summary I suggest trying different tools. Some tools even though they are supposed to be writing text files put invisible stuff into the text. Notoriously this occurs with the Unicode BOM which will halt any php file that tries to implement sessions or namespaces. Whilst this is no solution I hope it helps.

Vulcan answered 18/3, 2012 at 10:27 Comment(1)
Both of your function definitions don't run in SQuirreL (3.5.3) against a PostgreSQL 9.3.4 database.Bawbee
A
2

This example worked for me with PostgreSQL 14.1 and HeidiSQL 9.4.0.5125

DROP TABLE IF EXISTS emp;
CREATE TABLE emp (
    empname           text NOT NULL,
    salary            integer
);

DROP TABLE IF EXISTS EMP_AUDIT;
CREATE TABLE emp_audit(
    operation         char(1)   NOT NULL,
    stamp             timestamp NOT NULL,
    userid            text      NOT NULL,
    empname           text      NOT NULL,
    salary integer
);

DELIMITER //
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $$
    BEGIN
        --
        -- Create a row in emp_audit to reflect the operation performed on emp,
        -- make use of the special variable TG_OP to work out the operation.
        --
        IF (TG_OP = 'DELETE') THEN
            INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
            RETURN OLD;
        ELSIF (TG_OP = 'UPDATE') THEN
            INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
            RETURN NEW;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*;
            RETURN NEW;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS emp_audit ON emp;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
    FOR EACH ROW EXECUTE PROCEDURE process_emp_audit();
Alvar answered 24/1, 2017 at 3:34 Comment(1)
I was facing this issue in HeidiSQL, Then tried with DBViewer, It worked without any change.Celebrate
S
1

I had the same problem with zeos and c++ builder. The solution in my case:
Change the property delimiter (usually ";") to another in the component (class) I used.

dm->ZSQLProcessor1->DelimiterType=sdGo;

Perhaps Ant have something similar.

Sagittal answered 28/9, 2012 at 11:47 Comment(0)
P
1

I know this question was asked a long time ago but I had kind of the same issue with a Postgresql script (run from Jenkins) using Ant's SQL Task.

I tried to run this SQL (saved in a file named audit.sql):

DROP SCHEMA IF EXISTS audit CASCADE
;
CREATE SCHEMA IF NOT EXISTS audit AUTHORIZATION faktum
;
CREATE FUNCTION audit.extract_interval_trigger () 
RETURNS trigger AS $extractintervaltrigger$
BEGIN
        NEW."last_change_ts" := current_timestamp;
        NEW."last_change_by" := current_user;
        RETURN NEW;
END;
$extractintervaltrigger$ LANGUAGE plpgsql
;

but got the error "unterminated dollar-quoted string". No problem running it from pgAdmin.

I found out that it is not the driver that split the script at every ";" but rather Ant.

At http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting I found the answer:

Ant eats double-$$ as part of its variable processing. You have to use $BODY$ (or similar) in the stored procs, and put the delimiter on its own line (with delimitertype="row"). Ant will cooperate then.

My Ant SQL script looks like this and it works:

<sql
    driver="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/jenkins"
    userid="user" password="*****"
    keepformat="true"
    autocommit="true"
    delimitertype="row"
    encoding="utf-8"
    src="audit.sql"
/>
Philomena answered 3/5, 2016 at 18:45 Comment(0)
H
0

I was receiving the same error because I had my semicolon in a new line like this:

WHERE colA is NULL
;

Make sure they are in a single line as

WHERE colA is NULL;
Horse answered 3/3, 2015 at 12:40 Comment(1)
That must be an issue with your client, because it's irrelevant to Postgres.Fermi
C
0

I got the same error below:

ERROR: unterminated quoted string at or near "'OK;

When I tried to create my_func() below:

CREATE FUNCTION my_func() RETURNS VOID 
AS $$
BEGIN
  RAISE INFO 'OK;
END;
$$ LANGUAGE plpgsql;

So, I put ' just after OK as shown below, then I could create my_func() without error:

CREATE FUNCTION my_func() RETURNS VOID 
AS $$
BEGIN        -- ↓
  RAISE INFO 'OK';
END;
$$ LANGUAGE plpgsql;
Coo answered 18/2 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.