Re: Enforcing serial uniqueness?

Поиск
Список
Период
Сортировка
От Steven Brown
Тема Re: Enforcing serial uniqueness?
Дата
Msg-id 44220900.7080901@ucsd.edu
обсуждение исходный текст
Ответ на Re: Enforcing serial uniqueness?  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Enforcing serial uniqueness?  (Jim Nasby <jnasby@pervasive.com>)
Список pgsql-general
Tom Lane wrote:
[...]
> I think the solution for you is to use BEFORE triggers as suggested
> upthread.  The BEFORE INSERT trigger function should be SECURITY DEFINER
> and owned by a user who has permission to NEXTVAL the sequence.  The id
> column should probably be declared plain integer (or bigint), not
> SERIAL, because there's no percentage in setting a default that's just
> going to be overwritten by the trigger.

Wrote this up today and it works great - currval() is still
session-local which I didn't expect would work with the sequence behind
a security definer and updated by trigger.  The only thing missing,
which is quite minor, is I can't detect on INSERT if the caller
attempted to override the default with the default itself (e.g, INSERT
INTO foo(id) VALUES(id's default)), so they won't properly get an
exception in that case.  Not sure if there's a way to catch that.

In case it helps anyone else running into this thread, here's the solution:


-- Create a sequence that your normal users can read but not update.
CREATE SEQUENCE foo_id_seq;
GRANT SELECT ON foo_id_seq TO GROUP (normal user group);

-- Create a table where 'id' will be treated as serial.
CREATE TABLE foo(id integer NOT NULL DEFAULT 0 PRIMARY KEY, something TEXT);
GRANT SELECT, INSERT, UPDATE, DELETE ON foo TO GROUP (normal user group);

-- On INSERT, fill id from the sequence - creator has UPDATE permission.
-- Block attempts to force the id.
CREATE OR REPLACE FUNCTION foo_id_insert_procedure() RETURNS trigger
SECURITY DEFINER AS '
   BEGIN
     IF NEW.id != 0 THEN
       RAISE EXCEPTION ''Setting id to a non-default is not allowed'';
     ELSE
       NEW.id := nextval(''foo_id_seq'');
     END IF;
     RETURN NEW;
   END;
' LANGUAGE plpgsql;

-- Block all UPDATEs to the id.
CREATE OR REPLACE FUNCTION foo_id_update_procedure() RETURNS trigger
SECURITY DEFINER AS '
   BEGIN
     IF NEW.id != OLD.id THEN
       RAISE EXCEPTION ''Setting id to a non-default is not allowed'';
     ELSE
       RETURN NEW;
     END IF;
   END;
' LANGUAGE plpgsql;

CREATE TRIGGER "foo_id_insert_trigger" BEFORE INSERT ON foo
FOR EACH ROW EXECUTE PROCEDURE foo_id_insert_procedure();

CREATE TRIGGER "foo_id_update_trigger" BEFORE UPDATE ON foo
FOR EACH ROW EXECUTE PROCEDURE foo_id_update_procedure();


В списке pgsql-general по дате отправления:

Предыдущее
От: Michael Glaesemann
Дата:
Сообщение: Re: index for inet and >> (contains) function
Следующее
От: "surabhi.ahuja"
Дата:
Сообщение: Re: partial resultset in java