错误:未终止的引用字符串在或附近

时间:2010-08-17 05:06:32

标签: postgresql ant triggers plpgsql

使用ANT执行下面显示的触发代码时,我收到错误

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

我能够通过PGADmin(由postgres提供)和命令行实用程序“psql”成功执行以下代码,并且添加了触发器功能但是在通过ANT执行时它每次都失败

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;

8 个答案:

答案 0 :(得分:45)

我在 liquibase 中遇到此错误,此页面是首批搜索结果之一,因此我想在此页面上分享我的解决方案:

您可以将整个sql放在单独的文件中,并将其包含在更改集中。 将splitStatements选项设置为false非常重要。

整个变更集看起来像

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

我总是喜欢在单独的文件中包含那些大的SQL部分(比如函数更新等)。 这样,在打开sql文件时就可以获得正确的语法高亮,而不必在一个文件中混用XML和SQL。


修改:正如评论中所提到的,值得注意的是the sql change也支持splitStatements选项(对于指出这一点而言,他们也是如此)。

答案 1 :(得分:26)

我对Liquibase使用的JDBC驱动程序有同样的问题。

似乎驱动程序以分号结束每个行,并将其作为单独的SQL命令运行。这就是为什么下面的代码将由JDBC驱动程序按以下顺序执行:

  1. CREATE OR REPLACE FUNCTION test(text) RETURNS VOID AS ' DECLARE tmp text
  2. BEGIN tmp := "test"
  3. END;
  4. ' LANGUAGE plpgsql
  5. 当然,这是无效的SQL并导致以下错误:

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

    要纠正此问题,您需要在每行以分号结束后使用反斜杠:

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

    或者,您可以将整个定义放在一行中。

答案 2 :(得分:3)

我正在使用HeidiSQL客户端,这是通过在CREATE OR REPLACE语句之前放置DELIMITER来解决的。还有一个&#39;一次发送批次&#39; HeidiSQL中的选项基本上可以达到同样的效果。

答案 3 :(得分:1)

此错误是作为用于连接服务器的特定客户端与函数形式之间的交互而产生的。举例说明:

以下代码将在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;

请注意,'begin'语句紧跟在'$'引用字符串之后。

下一个代码将暂停除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;

第二个例子中的关键区别是“声明”部分。使用反斜杠的策略会引起PgAdmin3的错误。

总之,我建议尝试不同的工具。有些工具即使它们应该写文本文件也会将不可见的东西放入文本中。众所周知,这会发生在Unicode BOM中,它将暂停任何试图实现会话或命名空间的php文件。 虽然这不是解决方案,但我希望它有所帮助。

答案 4 :(得分:1)

我遇到了与zeos和c ++ builder相同的问题。 在我的情况下的解决方案:
将属性定界符(通常为“;”)更改为我使用的组件(类)中的另一个。

dm->ZSQLProcessor1->DelimiterType=sdGo;

也许Ant有类似的东西。

答案 5 :(得分:1)

这个例子适用于PostgreSQL 14.1和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();

答案 6 :(得分:0)

我收到同样的错误,因为我在这样的新行中加了分号:

WHERE colA is NULL
;

确保它们在一行中

WHERE colA is NULL;

答案 7 :(得分:0)

我知道很久以前就问过这个问题但是我使用Ant的SQL任务在Postgresql脚本(从Jenkins运行)中遇到了同样的问题。

我尝试运行此SQL(保存在名为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
;

但得到了错误&#34;未终止的美元引用字符串&#34;。从pgAdmin运行它没问题。

我发现每个&#34 ;;&#34;都不是驱动程序分割脚本的。而是Ant。

http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting我找到了答案:

  

Ant吃双倍 - $$作为其变量处理的一部分。你必须使用   存储过程中的$ BODY $(或类似),并将分隔符放在其上   自己的行(使用delimitertype =&#34; row&#34;)。那时Ant会合作。

我的Ant SQL脚本看起来像这样,它可以工作:

<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"
/>