PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como usar a entrada de texto como nome(s) de coluna em uma função Postgres?


A passagem de vários nomes de coluna como string concatenada para execução dinâmica requer urgentemente a descontaminação. Sugiro um VARIADIC parâmetro de função em vez disso, com identificadores corretamente citados (usando quote_ident() nesse 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 aqui

O especificador de formato %I para formato() lida com um único identificador. Você precisa trabalhar mais para vários identificadores, especialmente para um número variável de identificadores 0-n. Esta implementação cita cada nome de coluna e adiciona apenas um , se algum nome de coluna foi passado. Portanto, funciona para todas as entradas possíveis , mesmo sem nenhuma entrada. Nota VARIADIC cols text[] =NULL como último parâmetro de entrada com NULL como valor padrão:

Relacionado:

Os nomes das colunas diferenciam maiúsculas de minúsculas neste contexto!

Peça seu exemplo (importante!):
SELECT select_by_txt(10,32,33,'col1', 'col2');

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

Chamada mais reveladora, com um nome de terceira coluna e intenção maliciosa (embora fútil):
SELECT select_by_txt(10,32,33,'col1', 'col2', $$col3'); DROP TABLE table1;--$$);

Sobre esse estranho nome de terceira coluna e injeção de SQL:

Sobre VAIRADIC parâmetros:

Usando um OUT parâmetro de simplicidade. Isso é totalmente opcional. Ver:

O que eu não fazer


Se você realmente confia na entrada para ser uma lista devidamente formatada de 1 ou mais nomes de coluna válidos em todos os momentos - e você afirmou que ...

Você poderia 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$;

(Como você pode ter tanta certeza de que a entrada sempre será confiável?)