sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Cómo usar la entrada de texto como nombre (s) de columna en una función de Postgres?

Pasar varios nombres de columna como una cadena concatenada para una ejecución dinámica requiere una descontaminación urgente. Sugiero un VARIADIC parámetro de función en su lugar, con identificadores debidamente citados (usando quote_ident() en este caso):

CREATE OR REPLACE FUNCTION select_by_txt(z int, x int, y int, VARIADIC cols text[] = NULL, OUT res text)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format(
$$
SELECT ST_AsMVT(mvtgeom, 'public.select_by_txt')
FROM  (
   SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), bounds.geom) AS geom%s
   FROM   table1 t
   JOIN  (SELECT ST_TileEnvelope($1, $2, $3)) AS bounds(geom)
          ON ST_Intersects(t.geom, ST_Transform(bounds.geom, 4326))
   ) mvtgeom
$$, (SELECT ', ' || string_agg(quote_ident (col), ', ') FROM unnest(cols) col)
   )
   INTO  res
   USING z, x, y;
END
$func$;

db<>fiddle aquí

El especificador de formato %I para format() se ocupa de un soltero identificador Tienes que trabajar más para múltiples identificadores, especialmente para un número variable de 0-n identificadores. Esta implementación cita cada nombre de columna y solo agrega un , si se han pasado nombres de columna. Entonces funciona para cada entrada posible , incluso ninguna entrada en absoluto. Nota VARIADIC cols text[] = NULL como último parámetro de entrada con NULL como valor predeterminado:

Relacionado:

¡Los nombres de las columnas distinguen entre mayúsculas y minúsculas en este contexto!

Llame para su ejemplo (¡importante!):

SELECT select_by_txt(10,32,33,'col1', 'col2');

Sintaxis alternativa:

SELECT select_by_txt(10,32,33, VARIADIC '{col1,col2}');

Llamada más reveladora, con un nombre en la tercera columna y una intención maliciosa (aunque fútil):

SELECT select_by_txt(10,32,33,'col1', 'col2', $$col3'); DROP TABLE table1;--$$);

Acerca de ese extraño nombre de la tercera columna y la inyección de SQL:

Acerca de VAIRADIC parámetros:

Usando un OUT parámetro por simplicidad. Eso es totalmente opcional. Ver:

Lo que yo no hacer

Si realmente confía en que la entrada sea una lista correctamente formateada de 1 o más nombres de columna válidos en todo momento, y afirmó que...

podrías simplificar:

CREATE OR REPLACE FUNCTION select_by_txt(z int, x int, y int, cols text, OUT res text)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format(
$$
SELECT ST_AsMVT(mvtgeom, 'public.select_by_txt')
FROM  (
   SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), bounds.geom) AS geom, %s
   FROM   table1 t
   JOIN  (SELECT ST_TileEnvelope($1, $2, $3)) AS bounds(geom)
          ON ST_Intersects(t.geom, ST_Transform(bounds.geom, 4326))
   ) mvtgeom
$$, cols
   )
   INTO  res
   USING z, x, y;
END
$func$;

(¿Cómo puede estar tan seguro de que la entrada siempre será confiable?)