-- Triggers with new and old rows
CREATE FUNCTION trigger_show_row() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
  IF tg_op IN ('DELETE', 'UPDATE') THEN
    RAISE INFO '%: old = %', TG_NAME, OLD;
  END IF;
  IF tg_op IN ('INSERT', 'UPDATE') THEN
    RAISE INFO '%: new = %', TG_NAME, NEW;
  END IF;
  IF tg_op = 'DELETE' THEN
    RETURN OLD;
  ELSE
    RETURN NEW;
  END IF;
END $$;
create table tab_generated (a int,
  b int generated always as identity,
  c int default 0,
  d int GENERATED ALWAYS AS (c * 2));
create trigger tab_before
  after insert or update or delete
  on tab_generated for each row
  execute procedure trigger_show_row();
create trigger tab_after
  before insert or update or delete
  on tab_generated for each row
  execute procedure trigger_show_row();
insert into tab_generated values (1);
update tab_generated set a = 2 where a = 1;
delete from tab_generated where a = 2;

-- ACLs with virtual column using functions
CREATE FUNCTION dummy_int(integer) RETURNS integer
AS 'select $1;'
LANGUAGE SQL
IMMUTABLE STRICT;
CREATE TABLE tab_generated_acl (
  a int PRIMARY KEY,
  b int GENERATED ALWAYS AS (dummy_int(a)));
REVOKE ALL ON FUNCTION dummy_int(int) FROM PUBLIC;
INSERT INTO tab_generated_acl VALUES (1);
CREATE ROLE popo;
GRANT SELECT (a, b) ON tab_generated_acl TO popo;
SET ROLE popo;
-- should not work as no permission to use function
SELECT * FROM tab_generated_acl;
RESET role;
-- With default expression
CREATE TABLE tab_default_acl (
  a int PRIMARY KEY,
  b int DEFAULT (dummy_int(1)));
GRANT INSERT (a, b) ON tab_default_acl TO popo;                                                                                                                          
SET ROLE popo;
-- should not work as no permission to use function
INSERT INTO tab_default_acl VALUES (1);
