PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Definire i nomi di tabelle e colonne come argomenti in una funzione plpgsql?

Devi difenderti da SQL injection ogni volta che trasformi l'input dell'utente in codice. Ciò include nomi di tabelle e colonne provenienti da cataloghi di sistema o dall'input diretto dell'utente allo stesso modo. In questo modo previeni anche eccezioni banali con identificatori non standard. In pratica sono tre metodi integrati:

1. format()

1° interrogazione, igienizzato:

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() richiede Postgres 9.1 o successivo. Usalo con il %I identificatore di formato.

Il solo nome della tabella potrebbe essere ambiguo. Potrebbe essere necessario fornire il nome dello schema per evitare di modificare accidentalmente la tabella errata. Correlati:

  • INSERT con nome tabella dinamica nella funzione trigger
  • In che modo il percorso_ricerca influenza la risoluzione dell'identificatore e lo "schema corrente"

A parte:aggiunta di più colonne con un unico ALTER TABLE il comando è più economico.

2. regclass

Puoi anche utilizzare un cast in una classe registrata (regclass ) per il caso speciale di esistente nomi di tabelle. Facoltativamente qualificato per lo schema. Questo non riesce immediatamente e senza problemi per i nomi di tabella che non sono validi e visibili all'utente chiamante. La prima query è stata disinfettata con un cast 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$;

Chiama:

SELECT foo('table_name');

Oppure:

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

A parte:considera di usare solo text invece di varchar(20) .

3. quote_ident()

La seconda query sanificata:

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

Per concatenazioni/interpolazioni multiple, format() è più pulito...

Risposte correlate:

  • Nome tabella come parametro di funzione PostgreSQL
  • Funzioni Postgres vs query preparate

Maiuscole/minuscole!

Tieni presente che gli identificatori senza virgolette non cast in minuscolo qui. Quando viene utilizzato come identificatore in SQL [Postgres esegue automaticamente il cast in minuscolo][7]. Ma qui passiamo stringhe per SQL dinamico. Quando è stato eseguito l'escape come dimostrato, gli identificatori di maiuscole e minuscole CaMel (come UserS ) saranno conservati virgolette ("UserS" ), proprio come altri nomi non standard come "name with space" "SELECT" ecc. Quindi, i nomi fanno distinzione tra maiuscole e minuscole in questo contesto.

Il mio consiglio permanente è di utilizzare esclusivamente identificatori legali minuscoli e di non preoccuparsene mai.

A parte:le virgolette singole sono per i valori, le virgolette doppie sono per gli identificatori. Vedi:

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