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

Definir nomes de tabelas e colunas como argumentos em uma função plpgsql?


Você deve se defender contra a injeção de SQL sempre que você transforma a entrada do usuário em código. Isso inclui nomes de tabelas e colunas provenientes de catálogos do sistema ou da entrada direta do usuário. Dessa forma, você também evita exceções triviais com identificadores não padrão. Existem basicamente três métodos embutidos:

1. format()


1ª consulta, higienizada:
CREATE OR REPLACE FUNCTION foo(_t text)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format('
   ALTER TABLE %I ADD COLUMN c1 varchar(20)
                , ADD COLUMN c2 varchar(20)', _t);
END
$func$;

format() requer Postgres 9.1 ou posterior. Use-o com o %I especificador de formato.

O nome da tabela sozinho pode ser ambíguo. Talvez seja necessário fornecer o nome do esquema para evitar alterar a tabela errada acidentalmente. Relacionado:
  • INSERIR com o nome da tabela dinâmica na função de gatilho
  • Como o search_path influencia a resolução do identificador e o "esquema atual"

Além:adicionar várias colunas com um único ALTER TABLE comando é mais barato.

2. regclass


Você também pode usar um cast para uma classe registrada (regclass ) para o caso especial de existente nomes de tabelas. Opcionalmente qualificado pelo esquema. Isso falha imediatamente e normalmente para nomes de tabela que não são válidos e visíveis para o usuário chamador. A primeira consulta corrigida com uma conversão para regclass :
CREATE OR REPLACE FUNCTION foo(_t regclass)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE 'ALTER TABLE ' || _t || ' ADD COLUMN c1 varchar(20)
                                   , ADD COLUMN c2 varchar(20)';
END
$func$;

Ligar:
SELECT foo('table_name');

Ou:
SELECT foo('my_schema.table_name'::regclass);

Além:considere usar apenas text em vez de varchar(20) .

3. quote_ident()


A 2ª consulta higienizada:
CREATE OR REPLACE FUNCTION foo(_t regclass, _c text)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE 'UPDATE ' || _t    -- sanitized with regclass
        || ' SET ' || quote_ident(_c) || ' = ''This is a test''';
END
$func$;

Para várias concatenações/interpolações, format() é mais limpo...

Respostas relacionadas:
  • Nome da tabela como parâmetro de função do PostgreSQL
  • Funções do Postgres x consultas preparadas

Diferencia maiúsculas de minúsculas!


Esteja ciente de que identificadores sem aspas não convertido para minúsculas aqui. Quando usado como identificador no SQL [Postgres converte para minúsculas automaticamente][7]. Mas aqui passamos strings para SQL dinâmico. Quando escapado conforme demonstrado, identificadores CaMel-case (como UserS ) será preservado por aspas duplas ("UserS" ), assim como outros nomes não padrão como "name with space" "SELECT" etc. Portanto, os nomes diferenciam maiúsculas de minúsculas neste contexto.

Meu conselho permanente é usar identificadores legais de letras minúsculas exclusivamente e nunca se preocupar com isso.

Além disso:aspas simples são para valores, aspas duplas são para identificadores. Ver:
  • https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS