Postgresql

我有一個 INSTEAD OF 觸發器,但是當我插入視圖時 PostgreSQL 仍然抱怨

  • April 16, 2021

我創建了一個視圖和一個 INSTEAD OF 觸發器,用於從該視圖中插入/更新/刪除。現在我嘗試向視圖中插入一些數據,而 PostgreSQL 返回以下錯誤(捷克語翻譯:“chyba”=“error”,“Stav SQL”=“SQL state”):

ERROR:  cannot insert into view "ukaz_lok"
DETAIL:  Views that return columns that are not columns of their base relation are not automatically updatable.
HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.
********** Chyba **********

ERROR: cannot insert into view "ukaz_lok"
Stav SQL: 55000

這發生在其他一些 INSTEAD OF 觸發器之前 - 這個錯誤只持續了一段時間(幾個小時?我不記得確切),然後 PostgreSQL 找到了觸發器。我嘗試了各種方法(更改觸發器、刪除所有觸發器和視圖並重新創建它們等),但我不確定什麼有效,只是某些有效。那麼究竟如何讓PostgreSQL辨識觸發器呢?

我在 Windows XP 32 位上有 PostgreSQL 9.3.4 和 pgAdmin III 1.18.1。以防萬一它是由程式碼中的某些東西引起的,這裡是:

CREATE TABLE lokalita
(
 kod_lok text NOT NULL,
 nazev text NOT NULL,
 katastr text NOT NULL,
 presnost integer NOT NULL,
 stred geometry(Point,4326) NOT NULL,
 rozsah geometry(Polygon,4326),
 CONSTRAINT pk_lok PRIMARY KEY (kod_lok),
 CONSTRAINT fk_pro_pr FOREIGN KEY (presnost)
     REFERENCES pro_presnost (id) MATCH SIMPLE
     ON UPDATE CASCADE ON DELETE CASCADE
)
WITH (
 OIDS=FALSE
);

CREATE OR REPLACE VIEW ukaz_lok AS
 SELECT
   l.kod_lok AS _kod,
   l.nazev AS _nazev,
   l.katastr AS _katastr,
   l.presnost AS _presnost,
   50.0 AS _sirka,
   14.0 AS _delka
 FROM lokalita l;


CREATE OR REPLACE FUNCTION osetri_lok() RETURNS trigger AS $$
BEGIN
 IF TG_OP = 'INSERT' THEN
   INSERT INTO lokalita (kod_lok, nazev, katastr, presnost, stred)
   VALUES(
     NEW._kod,
     NEW._nazev,
     NEW._katastr,
     NEW._presnost,
     ST_GeomFromText('POINT('|| NEW.delka ||' '|| NEW.sirka ||')', 4326)
   );
 ELSIF TG_OP = 'UPDATE' THEN
   UPDATE lokalita 
   SET
     nazev = NEW._nazev,
     katastr = NEW._katastr,
     presnost = NEW._presnost,
     stred = ST_GeomFromText('POINT('|| NEW.delka ||' '|| NEW.sirka ||')', 4326)
   WHERE lod_lok = OLD._kod;
 ELSIF TG_OP = 'DELETE' THEN
   DELETE FROM lokalita WHERE kod_lok = OLD._kod;
 END IF;
 RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER osetri_lok
INSTEAD OF DELETE ON ukaz_lok
FOR EACH ROW
 EXECUTE PROCEDURE osetri_lok();

INSERT INTO ukaz_lok (_kod, _nazev, _katastr, _presnost, _delka, _sirka)
 VALUES(
 'Kote2',
 'Kotěhůlky 2',
 'Kotěhůlky',
 2,
 14.060316,
 50.432044
);

您的程式碼中有一個觸發器INSTEAD OF DELETE,但沒有INSTEAD OF INSERT。也創造它。

可以將其設為觸發器INSTEAD OF INSERT OR DELETE,但我建議將觸發器函式用於INSERTDELETE單獨的觸發器。簡化功能程式碼(不再需要IF TG_OP = 等)。

還要考慮手冊中的這句話

行級INSTEAD OF觸發器應該返回NULL以指示它沒有修改視圖的基礎基表中的任何數據,或者它應該返回傳入的視圖行( NEW用於INSERTUPDATE操作的OLD行,或用於 DELETE操作的行)。非空返回值用於表示觸發器在視圖中執行了必要的數據修改。這將導致受命令影響的行數的計數增加。僅對於INSERTUPDATE操作,觸發器可以NEW在返回之前修改行。這將更改INSERT RETURNINGor返回的數據UPDATE RETURNING,並且當視圖顯示的數據與提供的數據不完全相同時很有用。

引用自:https://dba.stackexchange.com/questions/63941