【问题标题】:ERROR: unterminated quoted string at or near错误:未终止的带引号的字符串在或附近
【发布时间】:2011-03-30 19:02:51
【问题描述】:

在使用 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;

【问题讨论】:

    标签: postgresql ant triggers plpgsql


    【解决方案1】:

    我在 liquibase 中遇到了这个错误,这个页面是最早的搜索结果之一,所以我想我在这个页面上分享我的解决方案:

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

    整个变更集看起来像

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

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


    编辑:如 cmets 中所述,值得注意的是 the sql change 也支持 splitStatements 选项(感谢 AndreyT 指出这一点)。

    【讨论】:

    • 我觉得有必要提一下,sql 也支持splitStatements="false"doc
    【解决方案2】:

    我对 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

    当然,这是无效的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;
    

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

    【讨论】:

    • 如果使用 LiquiBase 格式的 SQL 文件,请尝试将此注释添加到您的 proc/function 文件 splitStatements:false。这对我有用。
    • 嗨,感谢您的回答。它帮助我解决了类似的错误。但是,我想补充一点,您的解决方法(添加反斜杠)仅在某些情况下有效。拆分成单独的语句是由更高级别的 jdbc 完成的,因此究竟什么被视为一行,什么不取决于堆栈中的其他软件。在我的例子中,使用 spring,反斜杠不起作用,但可以指定不同的分隔符。
    • @andrewcooke 请问,你如何指定不同的分隔符来解决这个问题?
    【解决方案3】:

    我正在使用 HeidiSQL 客户端,通过在 CREATE OR REPLACE 语句之前放置 DELIMITER // 解决了这个问题。 HeidiSQL 中还有一个“一次性发送批处理”选项,基本上可以实现相同的目的。

    【讨论】:

    • 一次性发送批处理有助于 HeidiSQL
    • 救生员,谢谢 - 我永远不会自己找到这个。该选项位于“执行 SQL”按钮的下拉菜单中。
    【解决方案4】:

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

    以下代码将在 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 文件。 虽然这不是解决方案,但我希望它有所帮助。

    【讨论】:

    • 您的两个函数定义都不能在 SQuirreL (3.5.3) 中针对 PostgreSQL 9.3.4 数据库运行。
    【解决方案5】:

    我在使用 zeos 和 c++ builder 时遇到了同样的问题。 我的解决方案:
    在我使用的组件(类)中将属性分隔符(通常是“;”)更改为另一个。

    dm->ZSQLProcessor1->DelimiterType=sdGo;
    

    也许 Ant 也有类似的东西。

    【讨论】:

      【解决方案6】:

      我知道很久以前有人问过这个问题,但我在使用 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
      ;
      

      但收到错误“未终止的美元引用字符串”。从 pgAdmin 运行它没问题。

      我发现不是驱动程序在每个“;”处拆分脚本而是蚂蚁。

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

      Ant 吃掉 double-$$ 作为其变量处理的一部分。你必须使用 $BODY$(或类似的)在存储过程中,并将分隔符放在其上 自己的行(使用 delimitertype="row")。蚂蚁会配合的。

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

      【讨论】:

        【解决方案7】:

        此示例适用于 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();
        

        【讨论】:

          【解决方案8】:

          我收到了同样的错误,因为我的分号在新行中,如下所示:

          WHERE colA is NULL
          ;
          

          确保它们在一行中

          WHERE colA is NULL;
          

          【讨论】:

          • 这一定是你的客户的问题,因为它与 Postgres 无关。
          猜你喜欢
          • 1970-01-01
          • 2013-09-26
          • 1970-01-01
          • 2015-12-14
          • 2016-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-13
          相关资源
          最近更新 更多