Come accedere al campo NUOVO o VECCHIO dato solo il nome del campo?


8

Sto scrivendo un trigger di convalida. Il trigger deve confermare che la somma di un array è uguale a un altro campo. Dal momento che ho molti esempi di questa convalida, voglio scrivere una singola procedura e creare più trigger, ognuno con un diverso set di campi da controllare.

Ad esempio, ho il seguente schema:

CREATE TABLE daily_reports(
     start_on date
   , show_id uuid
   , primary key(start_on, show_id)

     -- _graph are hourly values, while _count is total for the report
   , impressions_count bigint not null
   , impressions_graph bigint[] not null

   -- interactions_count, interactions_graph
   -- twitter_interactions_count, twitter_interactions_graph
);

La convalida deve confermare ciò impressions_count = sum(impressions_graph).

Sono bloccato perché non so come accedere in modo dinamico a un campo NEWdall'interno di plpgsql:

CREATE FUNCTION validate_sum_of_array_equals_other() RETURNS TRIGGER AS $$
DECLARE
  total bigint;
  array_sum bigint;
BEGIN
  -- TG_NARGS = 2
  -- TG_ARGV[0] = 'impressions_count'
  -- TG_ARGV[1] = 'impressions_graph'

  -- How to access impressions_count and impressions_graph from NEW?
  RETURN NEW;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER validate_daily_reports_impressions
ON daily_reports BEFORE INSERT OR UPDATE
FOR EACH ROW EXECUTE
  validate_sum_of_array_equals_other('impressions_count', 'impressions_graph');

Ho provato a eseguire comandi dinamici facendo EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0], ma PL / PGsql si lamenta che NEW è una relazione sconosciuta.

Mi rivolgo specificamente a PostgreSQL 9.1.


Cross inviato alla mailing list generale di PostgreSQL postgresql.org/message-id/…
François Beausoleil

AFAIK al momento l'unico modo per accedere dinamicamente ai campi all'interno NEWè quello di utilizzare hstore(NEW)e quindi accedere ai campi come hstorevalori digitati dal nome della colonna. Il che fa schifo, perché poi sono tutti lanciati texte se vuoi lavorare con loro nel loro tipo originale devi ricacciarli. In alternativa, è possibile scrivere un trigger in un altro linguaggio procedurale come PL / Python che ha un supporto migliore per l'accesso dinamico ai record.
Craig Ringer,

@CraigRinger: Beh, no. Sei troppo pessimista oggi. C'è un modo con SQL dinamico.
Erwin Brandstetter,

Risposte:


14

In realtà, poiché NEWè un tipo composito ben definito, puoi semplicemente accedere a qualsiasi colonna con notazione di attributo semplice e chiara. Lo stesso SQL non consente identificatori dinamici (nomi di tabelle o colonne, ecc.). Ma puoi usare SQL dinamico conEXECUTE in una funzione PL / pgSQL.

dimostrazione

CREATE OR REPLACE FUNCTION trg_demo1()
  RETURNS TRIGGER AS
$func$
DECLARE
   _col_value text;
   _col_name  text := quote_ident(TG_ARGV[0]);  -- escape identifier
BEGIN
   EXECUTE format('SELECT ($1).%s::text', _col_name)
   USING NEW
   INTO  _col_value;

   -- do something with _col_value ...

   RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', _col_name, _col_value;

   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

Il cast a textè facoltativo. Usandolo, perché funziona universalmente. Se conosci il tipo, puoi lavorare senza trasmettere ...

Usando format()con %s, perché l'identificatore è già sfuggito a quel punto.
Altrimenti, utilizzare format()con %Iper salvaguardare dall'iniezione SQL.

In alternativa , in Postgres 9.3 o versioni successive, puoi convertire NEWin JSON con to_json()e accedere alle colonne come chiavi:

CREATE OR REPLACE FUNCTION trg_demo2()
  RETURNS TRIGGER AS
$func$
DECLARE
   _col_value text := to_json(NEW) ->> TG_ARGV[0];  -- no need to escape identifier
BEGIN
   RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', TG_ARGV[0], _col_value;
   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

Poiché il nome della colonna non è concatenato in una stringa SQL, non è possibile eseguire l'iniezione SQL e non è necessario eseguire l'escape del nome.

db <> violino qui (con EXCEPTIONinvece di NOTICErendere visibile l'effetto).

Relazionato:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.