2010-08-17 7 views
30

Mientras se ejecuta el código de activación por debajo se muestra usando ANT estoy consiguiendo el errorERROR: cita cadena sin terminar en o cerca de

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

soy capaz de ejecutar con éxito el código de abajo a través PgAdmin (proporcionado por postgres) y la línea de comandos se añade utilidad "psql" y la función de disparo, pero mientras se ejecuta a través de ANT que falla cada

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; 

Respuesta

25

que tenía el mismo problema con el controlador JDBC utilizado por Liquibase.

Parece que el controlador explota cada línea terminada por un punto y coma y la ejecuta como un comando SQL por separado. Es por ello que el siguiente código será ejecutado por el controlador JDBC en la siguiente secuencia:

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

Por supuesto, esto es SQL no válido y causa el siguiente error:

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

Para corregir esto, es necesario utilizar barras invertidas después de cada línea terminó con punto y coma:

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

Como alternativa, se puede colocar toda la definición de una línea.

+17

Si utiliza un archivo SQL con formato LiquiBase intente agregar este comentario a su proc/función fil e splitStatements: falso. Eso hizo el truco para mí. – Kuberchaun

+0

hola, gracias por esta respuesta. me ayudó con un error similar. sin embargo, quería agregar que su solución alternativa (agregar las barras diagonales inversas) solo funciona en algunos casos. la división en declaraciones separadas se realiza mediante un nivel más alto que jdbc, por lo que exactamente lo que se toma como una línea y lo que no depende de otro software en la pila.en mi caso, utilizando la primavera, la barra diagonal inversa no funciona, pero fue posible especificar un separador diferente. –

+1

@andrewcooke Por favor, ¿cómo se especifica un separador diferente para resolver el problema? –

1

Este error surge como una interacción entre el cliente particular utilizado para conectarse al servidor y la forma de la función. Para ilustrar:

El siguiente código se ejecutará sin accidentes en Netbeans 7, ardilla, 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; 

Tenga en cuenta que el 'begin' declaración se produce inmediatamente después de la '$' cadena entre comillas.

El siguiente código detendrá todos los clientes anteriores excepto 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 diferencia crucial en el segundo ejemplo es la sección 'declarar'. La táctica de usar barras diagonales provoca un error con PgAdmin3.

En resumen, sugiero probar diferentes herramientas. Algunas herramientas, aunque se supone que escriben archivos de texto, colocan elementos invisibles en el texto. Notoriamente, esto ocurre con la lista de materiales Unicode que detendrá cualquier archivo php que intente implementar sesiones o espacios de nombres. Si bien esta no es una solución, espero que ayude.

+0

Ambas definiciones de funciones no se ejecutan en SQuirreL (3.5.3) en una base de datos PostgreSQL 9.3.4. – zb226

1

Tuve el mismo problema con zeos y C++ builder. La solución en mi caso:
Cambie el delimitador de propiedades (generalmente ";") a otro en el componente (clase) que utilicé.

dm->ZSQLProcessor1->DelimiterType=sdGo; 

Quizás Ant tenga algo similar.

33

me encontré con este error en Liquibase y esta página ha sido uno de los primeros resultados de la búsqueda así que supongo que compartir mi solución a esta página:

Usted puede poner toda su SQL en un archivo separado para insertar esta en el conjunto de cambios. Es importante establecer la opción splitStatements en false.

Todo el conjunto de cambios sería parecido a

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

siempre me gusta tener esas grandes piezas de SQL (como actualizaciones de función y tal) en archivos separados. De esta forma se obtiene un resaltado de sintaxis adecuado al abrir el archivo sql y no tiene que entremezclar XML y SQL en un solo archivo.


Editar: como se ha mencionado en los comentarios de su pena señalar que the sql change soporta la opción splitStatements así (gracias a AndreyT por señalarlo).

+7

Creo que es necesario mencionar que 'sql' admite' splitStatements = "false" 'también. [doc] (http://www.liquibase.org/documentation/changes/sql.html) – AndreyT

1

Estoy utilizando el cliente HeidiSQL y esto se solucionó colocando DELIMITER // antes de la instrucción CREATE OR REPLACE. También hay una opción 'Enviar lote de una vez' en HeidiSQL que esencialmente logra lo mismo.

0

que estaba recibiendo el mismo error porque tenía mi punto y coma en una nueva línea como la siguiente:

WHERE colA is NULL 
; 

Asegúrese de que estén en una sola línea como

WHERE colA is NULL; 
+0

Eso debe ser un problema con su cliente, porque es irrelevante para Postgres. –

0

Sé que esta pregunta era hace mucho tiempo, pero tuve el mismo problema con un script de Postgresql (ejecutado desde Jenkins) usando la Tarea de SQL de Ant.

Traté de correr este SQL (guardado en un archivo llamado 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 
; 

pero tiene el error "cadena dólar cotizado sin terminar". No hay problema ejecutando desde pgAdmin.

Descubrí que no es el controlador el que divide el script en cada ";" sino más bien Ant.

En http://grokbase.com/t/postgresql/pgsql-jdbc/06cjx3s3y0/ant-sql-tag-for-dollar-quoting he encontrado la respuesta:

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.

Mi secuencia de comandos SQL hormiga tiene este aspecto y funciona:

<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

Este ejemplo me funcionó con PostgreSQL 14.1 y 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(); 
Cuestiones relacionadas