Làm cách nào để truy cập trường MỚI hoặc OLD chỉ được cung cấp tên của trường?


8

Tôi đang viết một kích hoạt xác nhận. Kích hoạt phải xác nhận rằng tổng của một mảng bằng với một trường khác. Vì tôi có nhiều phiên bản xác thực này, tôi muốn viết một thủ tục duy nhất và tạo nhiều kích hoạt, mỗi kích hoạt có một bộ trường khác nhau để kiểm tra.

Ví dụ, tôi có lược đồ sau:

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

Việc xác nhận phải xác nhận rằng impressions_count = sum(impressions_graph).

Tôi bị kẹt vì tôi không biết cách truy cập động một trường NEWtừ bên trong 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');

Tôi đã thử Thực thi các lệnh động bằng cách thực hiện EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0], nhưng PL / PGsql phàn nàn rằng MỚI là một mối quan hệ không xác định.

Tôi đặc biệt nhắm mục tiêu PostgreSQL 9.1.


Cross được đăng lên danh sách gửi thư chung của PostgreSQL postgresql.org/message-id/ory
François Beausoleil

AFAIK cách duy nhất để truy cập động các trường trong NEWthời điểm này là sử dụng hstore(NEW)và sau đó truy cập vào các trường dưới dạng hstorecác giá trị được khóa theo tên cột. Thật tệ, bởi vì sau đó tất cả họ đều được chọn textvà nếu bạn muốn làm việc với họ theo kiểu ban đầu, bạn phải bỏ họ lại. Thay phiên, bạn có thể viết một trình kích hoạt bằng ngôn ngữ thủ tục khác như PL / Python có hỗ trợ tốt hơn cho truy cập bản ghi động.
Craig Ringer

@CraigRinger: Vâng, không. Hôm nay bạn quá bi quan. Có một cách với SQL động.
Erwin Brandstetter

Câu trả lời:


14

Trên thực tế, vì NEWlà một kiểu hỗn hợp được xác định rõ, bạn chỉ có thể truy cập bất kỳ cột nào với ký hiệu thuộc tính đơn giản và đơn giản. Bản thân SQL không cho phép các định danh động (tên bảng hoặc cột, v.v.). Nhưng bạn có thể sử dụng SQL động vớiEXECUTE hàm PL / pgSQL.

Bản giới thiệu

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;

Các diễn viên textlà tùy chọn. Sử dụng nó, bởi vì nó hoạt động phổ biến. Nếu bạn biết loại, bạn có thể làm việc mà không cần đúc ...

Sử dụng format()với %s, bởi vì định danh đã được thoát tại thời điểm đó.
Khác, sử dụng format()với %Iđể bảo vệ chống lại tiêm SQL.

Ngoài ra , trong Postgres 9.3 trở lên, bạn có thể chuyển đổi NEWsang JSON với to_json()và truy cập các cột dưới dạng các khóa:

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;

Vì tên cột không được nối vào một chuỗi SQL, nên không thể thoát SQL và tên này không cần phải thoát.

db <> fiddle ở đây ( EXCEPTIONthay vì NOTICEđể hiển thị hiệu ứng).

Liên quan:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.