ОШИБКА: неиспользуемая строка с титром или рядом

Выполняя приведенный ниже код запуска с помощью 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;

Ответ 1

Я столкнулся с этой ошибкой в ​​ 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 для указания этого).

Ответ 2

У меня была та же проблема с драйвером JDBC, используемым Liquibase.

Кажется, что драйвер взрывает каждую строку, заканчивающуюся точкой с запятой, и запускает ее как отдельную команду SQL. Вот почему приведенный ниже код будет выполняться драйвером JDBC в следующей последовательности:

  • CREATE OR REPLACE FUNCTION test(text) RETURNS VOID AS ' DECLARE tmp text
  • BEGIN tmp := "test"
  • END;
  • ' 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;

В качестве альтернативы вы можете поместить все определение в одну строку.

Ответ 3

Эта ошибка возникает как взаимодействие между конкретным клиентом, используемым для подключения к серверу, и формой функции. Чтобы проиллюстрировать:

Следующий код будет работать без потерь в 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, которая остановит любой php файл, который пытается реализовать сеансы или пространства имен. Хотя это не решение, я надеюсь, что это поможет.

Ответ 4

У меня была такая же проблема с zeos и С++ builder. Решение в моем случае:
Измените разделитель свойств (обычно ";" ) на другой в компоненте (классе), который я использовал.

dm->ZSQLProcessor1->DelimiterType=sdGo;

Возможно, Ant имеет что-то подобное.

Ответ 5

Я использую клиент HeidiSQL, и это было решено, поставив DELIMITER//перед оператором CREATE OR REPLACE. В HeidiSQL есть опция "Отправить пакет за один раз", которая по сути достигает того же.

Ответ 6

Этот пример работал у меня с 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();

Ответ 7

Я получал ту же ошибку, потому что у меня была точка с запятой в новой строке:

WHERE colA is NULL
;

Убедитесь, что они находятся в одной строке как

WHERE colA is NULL;

Ответ 8

Я знаю, что этот вопрос задавался давно, но у меня была такая же проблема с Postgresql script (выполняется от Jenkins) с помощью Ant SQL Task.

Я попытался запустить этот 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
;

но получил ошибку "unterminated knot-quoted string". Нет проблем с запуском его из pgAdmin.

Я узнал, что это не драйвер, который разбивает script на каждом ";" а скорее Ant.

В http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting Я нашел ответ:

Ant ест двойной - $$ как часть обработки переменной. Вы должны использовать $BODY $(или аналогичный) в хранимых процедурах и помещает разделитель на свой собственной линией (с delimitertype = "row" ). Ant будет сотрудничать тогда.

Мой Ant SQL script выглядит так: он работает:

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