2010-07-15 16 views
5

Escribo secuencia de comandos PSQL y el uso de variables (para psql --variable clave = valor sintaxis de la línea de comandos).¿Hay alguna sintaxis de escape para la variable psql dentro de las funciones de PostgreSQL?

Esto funciona perfectamente para alcance de nivel superior como select * from: clave, pero creo funciones con el script y necesito valor variable dentro de ellas.

Por lo tanto, la sintaxis como

create function foo() returns void as 
$$ 
declare 
begin 
    grant select on my_table to group :user; 
end; 
$$ 
language plpgsql; 

falla en : usuario.

Por lo que yo entiendo, las variables psql es una función de sustitución de macro simple, pero no procesa los cuerpos de las funciones. ¿Hay alguna sintaxis de escape para tales casos? Alrededor de : usuario con $$ funciona con respecto a la sustitución, pero psql falla en $$.

¿Hay alguna otra manera de hacer esto además del procesamiento macro independiente (sed, awk, etc.)?

+0

¿Puede tener la función foo tomar un parámetro y usarlo? – Fosco

Respuesta

7

Las variables de PSQL SET no están interpoladas dentro de las cadenas entre comillas. No sé con certeza, pero creo que no hay escapatoria u otro truco para activar la interpolación variable SET allí.

Uno podría pensar que podría hacer coincidir un :user sin comillas entre dos tramos de PL/pgSQL cotizados en dólares para obtener el efecto deseado. Pero esto no parece funcionar ... Creo que la sintaxis requiere una sola cadena y no una expresión que concatene cadenas. Podría confundirse con eso.

De todos modos, eso no importa. Hay otro enfoque (como señaló Pasco): escriba el procedimiento almacenado para aceptar un argumento PL/pgSQL. Esto es lo que se vería.

CREATE OR REPLACE FUNCTION foo("user" TEXT) RETURNS void AS 
$$ 
BEGIN 
     EXECUTE 'GRANT SELECT ON my_table TO GROUP ' || quote_ident(user); 
END;  
$$ LANGUAGE plpgsql; 

Notas sobre esta función:

  1. EXECUTE genera una adecuada GRANT en cada invocación usando el procedimiento de nuestro argumento. La sección del manual de PG llamada "Executing Dynamic Commands" explica EXECUTE en detalle.
  2. El argumento de la declaración de procedimiento user debe estar doblemente citado. Las comillas dobles fuerzan que se interprete como un identificador.

Una vez que defina la función de esta manera, puede invocarla utilizando variables PSQL interpoladas. Aquí hay un resumen.

  1. Ejecute psql --variable user="'whoever'" --file=myscript.sql. ¡Se requieren comillas simples alrededor del nombre de usuario!
  2. En myscript.sql, defina la función como arriba.
  3. En myscript.sql, ponga select foo(:user);. Aquí es donde confiamos en esas comillas simples que ponemos en el valor de user.

Aunque esto parece funcionar, me parece algo squirrely.Pensé que las variables SET estaban pensadas para la configuración en tiempo de ejecución. Llevar datos en SET parece extraño.

Editar: he aquí una razón concreta para no uso SET variables. Desde la página de manual: "Estas asignaciones se realizan durante una etapa muy temprana de inicio, por lo que las variables reservadas para fines internos pueden sobrescribirse más tarde". Si Postgres decidió usar una variable llamada user (o lo que elija), podría sobrescribir el argumento del script con algo que nunca pensó. De hecho, psql ya toma USER por sí mismo; esto solo funciona porque SET distingue entre mayúsculas y minúsculas. ¡Esto casi rompió las cosas desde el principio!

+1

debe usar EXECUTE 'GRANT SELECT ON my_table TO GROUP' || quote_ident (usuario); Creo. – rfusca

+0

@rfusca, sí, no puedo hacer daño para desinfectarlo! –

4

Puede usar el método que Dan LaRocque describe para hacer que trabaje de forma hack'ish (no es un golpe para Dan), pero psql no es realmente su amigo para hacer este tipo de trabajo (suponiendo que este tipo de trabajo es de scripting y no cosas únicas). Si usted tiene una función, volver a escribir para tomar un parámetro de este modo:

create function foo(v_user text) returns void as 
$$ 
begin 
    execute 'grant select on my_table to group '||quote_ident(v_user); 
end; 
$$ 
language plpgsql; 

(quote_ident() lo hace por lo que no tiene que asegurar las comillas dobles, que se encarga de todo eso.)

Pero luego use un lenguaje de scripting real como Perl, Python, Ruby, etc. que tenga un driver Postgres para invocar su función con un parámetro.

Psql tiene su lugar, no estoy seguro de que sea esto.

+0

Acepto que usar psql 'SET' como este es probablemente un peligro de mantenimiento. Compartí tus dudas cuando escribí mi respuesta también. Hay un pequeño párrafo al final que da una razón concreta * por qué * es arriesgado. –

+0

@Dan LaRocque: Sí, es por eso que no sentí la necesidad de reiterar eso y por qué incluso voté a favor su respuesta. :) – rfusca

Cuestiones relacionadas