web-dev-qa-db-fra.com

ERREUR: chaîne entre guillemets non terminée à ou près de

Lors de l'exécution du code de déclenchement ci-dessous à l'aide d'ANT, j'obtiens l'erreur

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

Je suis en mesure d'exécuter avec succès le code ci-dessous via PGADmin (fourni par postgres) et l'utilitaire de ligne de commande "psql" et la fonction de déclenchement est ajoutée mais lors de l'exécution via ANT, elle échoue à chaque fois

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;
37
Anuj

J'ai rencontré cette erreur dans liquibase et cette page a été l'un des premiers résultats de recherche, donc je suppose que je partage ma solution sur cette page:

Vous pouvez mettre tout votre sql dans un fichier séparé et l'inclure dans le changeset. Il est important de définir l'option splitStatements sur false.

L'ensemble des modifications ressemblerait alors à

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

J'aime toujours avoir ces grosses parties SQL (comme les mises à jour de fonctions et autres) dans des fichiers séparés. De cette façon, vous obtenez une mise en évidence de la syntaxe appropriée lors de l'ouverture du fichier sql et n'avez pas à mélanger XML et SQL dans un seul fichier.


Edit : comme mentionné dans les commentaires, il convient de noter que le changement de sql prend en charge le splitStatements option également (merci à AndreyT pour l'avoir signalé).

47
Friedrich Große

J'ai eu le même problème avec le pilote JDBC utilisé par Liquibase.

Il semble que le pilote explose chaque ligne terminée par un point-virgule et l'exécute comme une commande SQL distincte. C'est pourquoi le code ci-dessous sera exécuté par le pilote JDBC dans l'ordre suivant:

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

Bien sûr, il s'agit d'un SQL non valide et provoque l'erreur suivante:

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

Pour corriger cela, vous devez utiliser des barres obliques inverses après chaque ligne terminée par un point-virgule:

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

Alternativement, vous pouvez placer la définition entière sur une seule ligne.

26
manRo

J'utilise le client HeidiSQL et cela a été résolu en plaçant DELIMITER // avant CREATE OR REPLACE statement. Il existe également une option "Envoyer le lot en une seule fois" dans HeidiSQL qui réalise essentiellement la même chose .

3
user3328634

Cette erreur se produit comme une interaction entre le client particulier utilisé pour se connecter au serveur et la forme de la fonction. Pour illustrer:

Le code suivant s'exécutera sans victime dans 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;

Veuillez noter que l'instruction "begin" vient immédiatement après la chaîne entre guillemets "$".

Le code suivant arrêtera tous les clients ci-dessus sauf 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;

La différence cruciale dans le deuxième exemple est la section "déclarer". Le stratagème consistant à utiliser des barres obliques inverses soulève une erreur avec PgAdmin3.

En résumé, je suggère d'essayer différents outils. Certains outils, même s'ils sont censés écrire des fichiers texte, insèrent des éléments invisibles dans le texte. Cela se produit notoirement avec la nomenclature Unicode qui arrêtera tout fichier php qui tente d'implémenter des sessions ou des espaces de noms. Bien que ce ne soit pas une solution, j'espère que cela aide.

1
huphos

J'ai eu le même problème avec zeos et c ++ builder. La solution dans mon cas:
Remplacez le délimiteur de propriété (généralement ";") par un autre dans le composant (classe) que j'ai utilisé.

dm->ZSQLProcessor1->DelimiterType=sdGo;

Peut-être que Ant a quelque chose de similaire.

1
fvel

Cet exemple a fonctionné pour moi avec PostgreSQL 14.1 et 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
David

Je sais que cette question a été posée il y a longtemps, mais j'ai eu un peu le même problème avec un script Postgresql (exécuté à partir de Jenkins) en utilisant la tâche SQL d'Ant.

J'ai essayé d'exécuter ce SQL (enregistré dans un fichier nommé 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
;

mais a obtenu l'erreur "chaîne sans terminaison entre guillemets". Aucun problème à l'exécuter à partir de pgAdmin.

J'ai découvert que ce n'est pas le pilote qui divise le script à chaque ";" mais plutôt Ant.

À http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting J'ai trouvé la réponse:

Ant mange double - $$ dans le cadre de son traitement variable. Vous devez utiliser $ BODY $ (ou similaire) dans les procs stockés et placer le délimiteur sur sa propre ligne (avec delimitertype = "row"). Ant coopérera alors.

Mon script Ant SQL ressemble à ceci et cela fonctionne:

<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
lisben

Je recevais la même erreur car j'avais mon point-virgule dans une nouvelle ligne comme celle-ci:

WHERE colA is NULL
;

Assurez-vous qu'ils sont sur une seule ligne

WHERE colA is NULL;
0
masoodg