错误:

问题描述 投票:0回答:9

使用 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
9个回答
60
投票

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

您可以将整个 sql 放在一个单独的文件中,并将其包含在变更集中。 将

splitStatements
选项设置为
false
很重要。

整个变更集看起来像

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

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


编辑:正如评论中提到的,值得注意的是

sql
更改也支持
splitStatements
选项(感谢AndreyT指出这一点)。


31
投票

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

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


10
投票

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


2
投票

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

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


2
投票

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

1
投票

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

dm->ZSQLProcessor1->DelimiterType=sdGo;

也许 Ant 也有类似的东西。


1
投票

我知道这个问题很久以前就被问过,但我使用 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"
/>

0
投票

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

WHERE colA is NULL
;

确保它们在一行中

WHERE colA is NULL;

0
投票

我在下面遇到了同样的错误:

错误:“'OK;”处或附近有未终止的引号字符串

当我尝试创建下面的

my_func()

时:

CREATE FUNCTION my_func() RETURNS VOID AS $$ BEGIN RAISE INFO 'OK; END; $$ LANGUAGE plpgsql;
所以,我将 

'

 放在 
OK
 之后,如下所示,然后我可以创建 
my_func()
 而不会出现错误:

CREATE FUNCTION my_func() RETURNS VOID AS $$ BEGIN -- ↓ RAISE INFO 'OK'; END; $$ LANGUAGE plpgsql;
    
© www.soinside.com 2019 - 2024. All rights reserved.