PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Définir les noms de table et de colonne comme arguments dans une fonction plpgsql ?

Vous devez vous défendre contre l'injection SQL chaque fois que vous transformez l'entrée de l'utilisateur en code. Cela inclut les noms de table et de colonne provenant des catalogues système ou de l'entrée directe de l'utilisateur. De cette façon, vous évitez également les exceptions triviales avec des identifiants non standard. Il y a essentiellement trois méthodes intégrées :

1. format()

1ère requête, épurée :

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() nécessite Postgres 9.1 ou version ultérieure. Utilisez-le avec le %I spécificateur de format.

Le nom de la table seul peut être ambigu. Vous devrez peut-être fournir le nom du schéma pour éviter de modifier accidentellement la mauvaise table. Connexe :

  • INSERT avec le nom de la table dynamique dans la fonction de déclenchement
  • Comment le search_path influence-t-il la résolution de l'identifiant et le "schéma actuel"

A part :ajouter plusieurs colonnes avec un seul ALTER TABLE commande est moins cher.

2. regclass

Vous pouvez également utiliser un cast vers une classe enregistrée (regclass ) pour le cas particulier de existant noms de tables. Facultativement qualifié de schéma. Cela échoue immédiatement et gracieusement pour les noms de table qui ne sont pas valides et visibles pour l'utilisateur appelant. La 1ère requête assainie avec un cast en 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$;

Appel :

SELECT foo('table_name');

Ou :

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

À part :pensez à n'utiliser que text au lieu de varchar(20) .

3. quote_ident()

La 2ème requête épurée :

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$;

Pour plusieurs concaténations / interpolations, format() est plus propre ...

Réponses associées :

  • Nom de table en tant que paramètre de fonction PostgreSQL
  • Fonctions Postgres vs requêtes préparées

Respect de la casse !

Sachez que les identifiants sans guillemets ne le sont pas mis en minuscules ici. Lorsqu'il est utilisé comme identifiant dans SQL [Postgres passe automatiquement en minuscules][7]. Mais ici nous passons des chaînes pour SQL dynamique. Lorsqu'ils sont échappés comme démontré, les identifiants CaMel-case (comme UserS ) seront conservés par des guillemets doubles ("UserS" ), tout comme d'autres noms non standard comme "name with space" "SELECT" etc. Par conséquent, les noms sont sensibles à la casse dans ce contexte.

Mon conseil permanent est d'utiliser exclusivement des identifiants légaux en minuscules et de ne jamais s'en soucier.

À part :les guillemets simples sont pour les valeurs, les guillemets doubles sont pour les identifiants. Voir :

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