2010-08-17 14 views
30

Podczas wykonywania poniżej pokazany kod aktywacji za pomocą ANT Dostaję błądBŁĄD: niezakończony cudzysłowie na lub w pobliżu

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

jestem w stanie z powodzeniem wykonać poniższy kod poprzez pgAdmin (Zapewnione przez PostgreSQL) i wiersz poleceń narzędzie „psql”, a funkcja wyzwalania dodaje ale podczas wykonywania przez ANT to nie spędzaj

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; 

Odpowiedz

25

miałem ten sam problem ze sterownikiem JDBC używanego przez Liquibase.

Wygląda na to, że sterownik wybucha każdą linię zakończoną średnikiem i uruchamia ją jako oddzielne polecenie SQL. Dlatego poniższy kod będzie wykonywany przez sterownik JDBC w następującej kolejności:

  1. CREATE OR REPLACE FUNCTION test(text) RETURNS VOID AS ' DECLARE tmp text
  2. BEGIN tmp := "test"
  3. END;
  4. ' LANGUAGE plpgsql

Oczywiście, to jest nieważne SQL i powoduje następujący błąd:

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

Aby rozwiązać ten problem, należy użyć backslashy po każdym wierszu zakończony średnikiem:

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

Alternatywnie, można umieścić całą definicję w jednej linii.

+17

Jeśli używasz sformatowanego pliku SQL LiquiBase, spróbuj dodać ten komentarz do pliku proc/function e splitStatements: false. To mi się udało. – Kuberchaun

+0

Cześć, dziękuję za tę odpowiedź. pomógł mi z podobnym błędem. jednak chciałem dodać, że twoje obejście (dodawanie odwróconych ukośników) działa tylko w niektórych przypadkach. podział na oddzielne zdania odbywa się na wyższym poziomie niż jdbc, czyli dokładnie to, co jest brane jako linia, a co nie zależy od innego oprogramowania w stosie.w moim przypadku, używając sprężyny, odwrócony ukośnik nie działa, ale możliwe było określenie innego separatora. –

+1

@andrewcooke Jak określić inny separator, aby rozwiązać problem? –

1

Ten błąd powstaje jako interakcja między konkretnym klientem używanym do łączenia się z serwerem a formularzem funkcji. Aby zilustrować:

Poniższy kod będzie działał bez wypadku w Netbeans 7, wiewiórka, 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; 

Należy pamiętać, że „rozpocząć” oświadczenie pochodzi bezpośrednio po „$” cudzysłowie.

Następny kod zatrzyma wszystkich powyższych klientów za wyjątkiem 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; 

Zasadniczą różnicą w drugim przykładzie jest sekcja "zadeklaruj". Sposób użycia back-slash powoduje błąd w PgAdmin3.

Podsumowując, sugeruję wypróbowanie różnych narzędzi. Niektóre narzędzia, nawet jeśli mają pisać pliki tekstowe, umieszczają niewidoczne rzeczy w tekście. Notorycznie dzieje się tak z BOM Unicode, który zatrzyma każdy plik php, który próbuje zaimplementować sesje lub przestrzenie nazw. Chociaż nie jest to rozwiązanie, mam nadzieję, że pomoże.

+0

Obie definicje funkcji nie działają w SQuirreL (3.5.3) w stosunku do bazy danych PostgreSQL 9.3.4. – zb226

1

Miałem ten sam problem ze zeos i C++ builderem. Rozwiązanie w moim przypadku:
Zmień ogranicznik właściwości (zwykle ";") na inny w używanym komponencie (klasie).

dm->ZSQLProcessor1->DelimiterType=sdGo; 

Być może Ant ma coś podobnego.

33

I napotkał ten błąd w liquibase i ta strona była jedną z pierwszych wyników wyszukiwania, więc myślę, dzielę rozwiązanie na tej stronie:

Możesz umieścić całą swoją sql w osobnym pliku i informuje o tym w zestawie zmian. Ważne jest ustawienie opcji splitStatements na false.

Cały changeset będzie wtedy wyglądać

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

Zawsze lubię mieć te duże części SQL (jak aktualizacjach funkcyjnych i takie) w osobnych plikach. W ten sposób otrzymasz poprawne podświetlanie składni podczas otwierania pliku sql i nie musisz mieszać XML i SQL w jednym pliku.


Edit: jak wspomniano w komentarzach warto zauważyć, że the sql change obsługuje opcję splitStatements jak również (thx do AndreyT za wskazanie, że obecnie).

+7

Myślę, że trzeba wspomnieć, co 'sql' obsługuje' splitStatements = "false" 'too. [Doc] (http://www.liquibase.org/documentation/changes/sql.html) – AndreyT

1

Używam klienta HeidiSQL i zostało to rozwiązane przez umieszczenie DELIMITER // przed instrukcją CREATE OR REPLACE. Istnieje również opcja "Wyślij wsad w jednym kroku" w HeidiSQL, która zasadniczo osiąga to samo.

0

ja otrzymywał ten sam błąd, ponieważ miałem średnik w nowej linii tak:

WHERE colA is NULL 
; 

Upewnij się, że są w jednej linii, jak

WHERE colA is NULL; 
+0

To musi być problem z klientem, ponieważ jest to bez znaczenia dla PostgreSQL. –

0

Wiem, że to pytanie było zadawane dawno temu, ale miałem taki sam problem ze skryptem PostgreSQL (uruchamianym z Jenkinsa) używając zadania SQL Anta.

próbowałem uruchomić to SQL (zapisane w pliku o nazwie 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 
; 

ale mam błąd „niezakończony dolara cudzysłowie”. Nie ma problemu z uruchomieniem go z pgAdmin.

Dowiedziałem się, że to nie sterownik dzieli skrypt na ";" ale raczej Ant.

Na http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting znalazłem odpowiedź:

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.

Mój Ant skrypt SQL wygląda i działa:

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

Przykład ten pracował dla mnie z PostgreSQL 14.1 i 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(); 
Powiązane problemy