Pregunta

My question is relative to a postgresql problem I encountered. I want to modify a database containing a newsgroup-like forum. Its messages describe a tree and are stored in a table as below :

\d messages
                                         Table « public.messages »
  Column   |            Type             |                          Modifiers
-----------+-----------------------------+------------------------------------------------------------------
 idmessage | integer                     | not NULL By default, nextval('messages_idmessage_seq'::regclass)
 title     | character varying(50)       | not NULL
 datemsg   | timestamp without time zone | By default, ('now'::text)::timestamp without time zone
 author    | character varying(10)       | By default, "current_user"()
 text      | text                        | not NULL
 idgroup   | integer                     | not NULL
 msgfather | integer                     |

(I translated the output from French, I hope I didn't add misinterpretation)

Title is the message title, datemsg its timestamp, author and text are quite explicit, idgroup is the discussion group identifier where the message is found and msgfather is another message this message anwsers to. The rule I want to implement on insert is about preventing the user to add an answer to a message with a different group from its father's (if it has one, as a new discussion starts with a new message posted without parent).

Now I have this view:

\d newmessage
           View « public.newmessage »
  Column   |         Type          | Modifiers
-----------+-----------------------+---------------
 idmessage | integer               |
 title     | character varying(50) |
 text      | text                  |
 idgroup   | integer               |
 msgfather | integer               |
View definition :
 SELECT messages.idmessage, messages.title, messages.text, messages.idgroup, messages.msgfather
   FROM messages;
Rules :
 ins_message AS
    ON INSERT TO newmessage DO INSTEAD  INSERT INTO messages (title, text, idgroup, msgfather)
  VALUES (new.title, new.text, new.idgroup, new.msgfather)

I can't change view or table as people already use the database, so I think I have to play with rules or triggers.

I tried to add to the view this rule below, without the expected effect:

CREATE OR REPLACE RULE ins_message_answer AS ON INSERT TO newmessage WHERE NEW.msgfather IS NOT NULL DO INSTEAD INSERT INTO messages( title, text, idgroup, msgfather) VALUES ( NEW.title, NEW.text, (SELECT idgroup FROM messages WHERE idmessage=new.msgfather), NEW.msgfather);

Even with this new rule, people can still answer a message and set this answer in another group.

I tried too to change the ins_message rule by adding WHERE NEW.msgfather IS NULL, but I then have the error that no rule is found as on insert in newmessage view when I try to add a message.

So how to achieve what I want to do? With a trigger? (I don't know how to do one with postgresql).

¿Fue útil?

Solución

First create an unique index for (idmessage, idgroup):

alter table messages add constraint messages_idmessage_idgroup_key
  unique (idmessage, idgroup);

This constraint is needed for msgfather_idgroup_fkey below as foreign key constraints require matching unique index and without it there'll be an ERROR: there is no unique constraint matching given keys for referenced table "messages".

Then add self referencing foreign key:

alter table messages add constraint msgfather_idgroup_fkey
  foreign key (msgfather,idgroup) references messages(idmessage,idgroup);

In passing:

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top