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