Re: [SQL] Rule Question

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: "Mitch Vincent" <mitch(at)venux(dot)net>
Cc: pgsql-sql(at)postgreSQL(dot)org
Subject: Re: [SQL] Rule Question
Date: 1999-12-19 22:52:20
Message-ID: 10078.945643940@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-sql

"Mitch Vincent" <mitch(at)venux(dot)net> writes:
> Created this function to do the totalling:

> create function total_up(int4) returns float8 as 'select fee_membership +
> fee_logins + fee_convention + fee_prints+ fee_hotlines + fee_postage +
> fee_ups + fee_late + fee_other1 + fee_other2 + fee_other3 + fee_pastdue from
> invoice where invoice_number = $1;' language 'sql';

> And this rule to call the function and update the invoice that has been
> updated, inserting the new total. :

> create rule total_invoice as on update to invoice where
> invoice_number=NEW.invoice_number DO update invoice set total =
> total_up(NEW.invoice_number) where invoice_number=NEW.invoice_number;

> However I get this when I do an update on an invoice:

> PostgreSQL Error: 1 (ERROR: query rewritten 10 times, may contain cycles )

Not sure, but I think the problem is that the rule is recursively
applied to itself --- it's defined to fire on any UPDATE to the invoice
table, and inside the rule you ask for another UPDATE to invoice, so
you got trouble. A rule has to reduce the given case to something
different.

I'd be inclined to do this with a trigger instead of a rule. To do it
with a trigger, you go ahead and define the total column as a real
column in the database, but then you put in a trigger that calculates
the correct value from the rest of the tuple whenever a tuple is
inserted or updated, overriding whatever the old value may have been
(or whatever the application tried to supply!). It'd look something
like this if you use plpgsql:

CREATE FUNCTION invoice_total_trigger() RETURNS OPAQUE AS '
BEGIN
NEW.total := NEW.fee_membership + NEW.fee_logins + ...;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER invoice_total_trigger BEFORE INSERT OR UPDATE ON invoice
FOR EACH ROW EXECUTE PROCEDURE invoice_total_trigger();

Another way is to leave the total column out of the underlying table,
and define a VIEW that includes all the underlying columns plus the
total, computing the total on-the-fly:
SELECT *, fee_membership + fee_logins + ... FROM invoice
This'd probably be better if you anticipate many more updates than
queries, but it'd be a loser if many more queries than updates.
Details left as an exercise for the student...

regards, tom lane

Browse pgsql-sql by date

  From Date Subject
Next Message Barry Baldwin 1999-12-20 01:47:06 MINUS emulation?
Previous Message Tom Lane 1999-12-19 22:38:56 Re: [SQL] New count() question