我有一个 INSTEAD OF 触发器,但是当我插入视图时 PostgreSQL 仍然抱怨

Pav*_* V. 5 postgresql trigger view plpgsql

我创建了一个视图和一个 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
Run Code Online (Sandbox Code Playgroud)

这发生在其他一些 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
);
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 10

INSTEAD OF DELETE的代码中有一个触发器,但没有INSTEAD OF INSERT。也创造那个。

可以将其设为 trigger INSTEAD OF INSERT OR DELETE,但我建议为INSERTandDELETE和单独的触发器使用单独的触发器功能。简化功能代码(不再需要IF TG_OP = 等)。

还要考虑手册中的引用

行级INSTEAD OF触发器应该返回NULL以指示它没有修改视图底层基表中的任何数据,或者它应该返回传入的视图行( NEW用于INSERTUPDATE操作的OLD行,或用于DELETE操作的行 )。非空返回值用于表示触发器在视图中执行了必要的数据修改。这将导致受命令影响的行数增加。仅对于INSERTUPDATE操作,触发器可能会NEW在返回之前修改该行。这将更改INSERT RETURNING或返回的数据UPDATE RETURNING,并且在视图不会显示与提供的完全相同的数据时很有用。