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

Nome tabella come parametro di funzione PostgreSQL

Questo può essere ulteriormente semplificato e migliorato:

CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
    LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
   INTO result;
END
$func$;

Chiama con nome qualificato per schema (vedi sotto):

SELECT some_f('myschema.mytable');  -- would fail with quote_ident()

Oppure:

SELECT some_f('"my very uncommon table name"');

Punti principali

Usa un OUT parametro per semplificare la funzione. È possibile selezionare direttamente il risultato dell'SQL dinamico al suo interno e il gioco è fatto. Non sono necessarie variabili e codice aggiuntivi.

EXISTS fa esattamente quello che vuoi. Ottieni true se la riga esiste o false altrimenti. Ci sono vari modi per farlo, EXISTS è in genere più efficiente.

Sembra che tu voglia un numero intero indietro, quindi ho lanciato il boolean risultato da EXISTS a integer , che restituisce esattamente quello che avevi. Vorrei restituire booleano invece.

Uso il tipo di identificatore oggetto regclass come tipo di input per _tbl . Questo fa tutto quote_ident(_tbl) o format('%I', _tbl) farebbe, ma meglio, perché:

  • .. impedisce l'iniezione SQL altrettanto bene.

  • .. fallisce immediatamente e con maggiore grazia se il nome della tabella non è valido / non esiste / è invisibile all'utente corrente. (Un regclass parametro è applicabile solo per esistente tabelle.)

  • .. funziona con nomi di tabelle qualificati per schema, dove un semplice quote_ident(_tbl) o format(%I) fallirebbero perché non possono risolvere l'ambiguità. Dovresti passare e sfuggire allo schema e ai nomi delle tabelle separatamente.

Funziona solo per esistente tabelle, ovviamente.

Uso ancora format() , perché semplifica la sintassi (e per dimostrare come viene utilizzata), ma con %s invece di %I . In genere, le query sono più complesse, quindi format() aiuta di più. Per il semplice esempio potremmo anche concatenare:

EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'

Non è necessario qualificare come tabella l'id colonna mentre c'è solo una singola tabella nel FROM elenco. Nessuna ambiguità possibile in questo esempio. Comandi SQL (dinamici) all'interno di EXECUTE avere un ambito separato , le variabili di funzione oi parametri non sono visibili lì, a differenza dei semplici comandi SQL nel corpo della funzione.

Ecco perché sempre sfuggire all'input dell'utente per SQL dinamico correttamente:

db<>gioca qui dimostrazione dell'iniezione SQL
Sqlfiddle vecchio