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

¿Definir nombres de tablas y columnas como argumentos en una función plpgsql?

Debe defenderse contra la inyección SQL siempre que convierta la entrada del usuario en código. Eso incluye nombres de tablas y columnas provenientes de catálogos del sistema o de la entrada directa del usuario por igual. De esta manera, también evita excepciones triviales con identificadores no estándar. Básicamente hay tres métodos integrados:

1. format()

1ra consulta, desinfectada:

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() requiere Postgres 9.1 o posterior. Úselo con el %I especificador de formato.

El nombre de la tabla solo puede ser ambiguo. Es posible que deba proporcionar el nombre del esquema para evitar cambiar la tabla incorrecta por accidente. Relacionado:

  • INSERTAR con el nombre de la tabla dinámica en la función de activación
  • ¿Cómo influye search_path en la resolución del identificador y el "esquema actual"?

Aparte:agregando múltiples columnas con una sola ALTER TABLE el comando es más barato.

2. regclass

También puede usar una conversión a una clase registrada (regclass ) para el caso especial de existente nombres de tablas Opcionalmente calificado por esquema. Esto falla inmediatamente y con gracia para los nombres de tablas que no son válidos ni visibles para el usuario que llama. La primera consulta desinfectada con un envío a 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$;

Llamar:

SELECT foo('table_name');

O:

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

Aparte:considere usar solo text en lugar de varchar(20) .

3. quote_ident()

La segunda consulta desinfectada:

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 múltiples concatenaciones/interpolaciones, format() es más limpio...

Respuestas relacionadas:

  • Nombre de tabla como parámetro de función de PostgreSQL
  • Funciones de Postgres frente a consultas preparadas

¡Sensible a mayúsculas y minúsculas!

Tenga en cuenta que los identificadores sin comillas no echado a minúsculas aquí. Cuando se usa como identificador en SQL [Postgres convierte a minúsculas automáticamente][7]. Pero aquí pasamos cadenas para SQL dinámico. Cuando se escapa como se muestra, los identificadores de caso de CaMel (como UserS ) se conservará entre comillas dobles ("UserS" ), al igual que otros nombres no estándar como "name with space" "SELECT" etc. Por lo tanto, los nombres distinguen entre mayúsculas y minúsculas en este contexto.

Mi consejo permanente es usar identificadores legales en minúsculas exclusivamente y nunca preocuparse por eso.

Aparte:las comillas simples son para valores, las comillas dobles son para identificadores. Ver:

  • https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS