Bagaimana cara mengakses bidang BARU atau LAMA hanya diberi nama bidang?


8

Saya sedang menulis pemicu validasi. Pemicu harus memvalidasi bahwa jumlah array sama dengan bidang lain. Karena saya memiliki banyak contoh validasi ini, saya ingin menulis satu prosedur dan membuat beberapa pemicu, masing-masing dengan set bidang yang berbeda untuk diperiksa.

Misalnya, saya memiliki skema berikut:

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

Validasi harus mengkonfirmasi hal itu impressions_count = sum(impressions_graph).

Saya macet karena saya tidak tahu cara mengakses bidang secara dinamis NEWdari dalam 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');

Saya mencoba Menjalankan Perintah Dinamis dengan melakukan EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0], tetapi PL / PGsql mengeluh bahwa BARU adalah hubungan yang tidak diketahui.

Saya secara khusus menargetkan PostgreSQL 9.1.


Cross memposting ke milis umum PostgreSQL postgresql.org/message-id/…
François Beausoleil

AFAIK satu-satunya cara untuk secara dinamis mengakses bidang dalam NEWsaat ini adalah dengan menggunakan hstore(NEW)dan kemudian mengakses bidang sebagai hstorenilai yang dikunci dengan nama kolom. Yang menyebalkan, karena mereka semua dilemparkan ke textdan jika Anda ingin bekerja dengan mereka dalam tipe asli Anda, Anda harus melemparkan mereka kembali. Sebagai alternatif, Anda dapat menulis pemicu dalam bahasa prosedural lain seperti PL / Python yang memiliki dukungan yang lebih baik untuk akses rekaman dinamis.
Craig Ringer

@CraigRinger: Ya, tidak. Kamu terlalu pesimis hari ini. Ada cara dengan SQL dinamis.
Erwin Brandstetter

Jawaban:


14

Sebenarnya, karena NEWmerupakan tipe komposit yang terdefinisi dengan baik, Anda dapat mengakses kolom apa saja dengan notasi atribut sederhana dan sederhana. SQL sendiri tidak memungkinkan pengidentifikasi dinamis (nama tabel atau kolom dll.). Tetapi Anda dapat menggunakan SQL dinamis denganEXECUTE fungsi PL / pgSQL.

Demo

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;

Para pemain textadalah opsional. Menggunakannya, karena bekerja secara universal. Jika Anda tahu tipenya, Anda dapat bekerja tanpa ...

Menggunakan format()dengan %s, karena pengidentifikasi sudah lolos pada saat itu.
Lain, gunakan format()dengan %Iuntuk melindungi terhadap injeksi SQL.

Atau , di Postgres 9.3 atau lebih baru, Anda dapat mengonversi NEWke JSON dengan to_json()dan mengakses kolom sebagai kunci:

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;

Karena nama kolom tidak digabungkan menjadi string SQL, injeksi SQL tidak mungkin, dan nama tidak perlu diloloskan.

db <> bermain-main di sini (dengan EXCEPTIONalih - alih NOTICEmembuat efeknya terlihat).

Terkait:

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.