错误:位于或附近未终止的带引号的字符串

发布于 2024-09-14 11:26:01 字数 719 浏览 2 评论 0原文

使用 ANT 执行下面所示的触发代码时,出现错误:

org.postgresql.util.PSQLException:错误:未终止的带引号的字符串位于或接近“'DECLARE超时整数” 排名: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;

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

悲念泪 2024-09-21 11:26:01

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

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

整个变更集看起来就像

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

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


编辑:正如评论中提到的,值得注意的是sqlchange 也支持 splitStatements 选项(感谢 AndreyT 指出这一点)。

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).

泅渡 2024-09-21 11:26:01

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

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

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.

橙味迷妹 2024-09-21 11:26:01

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

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.

漆黑的白昼 2024-09-21 11:26:01

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

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

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.

谈场末日恋爱 2024-09-21 11:26:01

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

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();
素年丶 2024-09-21 11:26:01

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

dm->ZSQLProcessor1->DelimiterType=sdGo;

也许蚂蚁金服也有类似的东西。

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.

如何视而不见 2024-09-21 11:26:01

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

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"
/>
貪欢 2024-09-21 11:26:01

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

WHERE colA is NULL
;

确保它们在一行中

WHERE colA is NULL;

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;
花期渐远 2024-09-21 11:26:01

我得到了同样的错误如下:

错误:“'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;

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文