Thread: conditional rule not applied

conditional rule not applied

From
Seb
Date:
Hi,

I'm trying to create a rule to be applied on update to a view that
consists of two joined tables.  Table 'shoes' below is left-joined with
table 'shoelaces' in the view 'footwear'.  I'd like to create a simple
update rule on the view, only if the value of a common column
corresponds to an inexistent record in 'shoelaces', so the result is an
INSERT into 'shoelaces' with the new record:

---<--------------------cut here---------------start------------------->---
CREATE TABLE shoes (
    sh_id serial PRIMARY KEY,
    sh_name text,
    sh_avail integer
);

CREATE TABLE shoelaces (
    sl_id serial PRIMARY KEY,
    sh_id integer REFERENCES shoes,
    sl_name text
);

INSERT INTO shoes (sh_name, sh_avail)
    VALUES ('sh1', 2), ('sh2', 0), ('sh3', 4), ('sh4', 3);

INSERT INTO shoelaces (sh_id, sl_name)
    VALUES (1, 'sl1'), (3, 'sl2');

SELECT * FROM shoes;

 sh_id | sh_name | sh_avail
-------+---------+----------
     1 | sh1     |        2
     2 | sh2     |        0
     3 | sh3     |        4
     4 | sh4     |        3

SELECT * FROM shoelaces;

 sl_id | sh_id | sl_name
-------+-------+---------
     1 |     1 | sl1
     2 |     3 | sl2
(2 rows)

CREATE VIEW footwear AS
SELECT sh.sh_id, sh_name, sh_avail, sl_name
FROM shoes sh LEFT JOIN shoelaces sl USING (sh_id);

SELECT * FROM footwear;

 sh_id | sh_name | sh_avail | sl_name
-------+---------+----------+---------
     1 | sh1     |        2 | sl1
     2 | sh2     |        0 |
     3 | sh3     |        4 | sl2
     4 | sh4     |        3 |
(4 rows)

CREATE RULE footwear_nothing_upd AS
    ON UPDATE TO footwear DO INSTEAD NOTHING;
CREATE RULE footwear_newshoelaces_upd AS
    ON UPDATE TO footwear
    WHERE NEW.sl_name <> OLD.sl_name AND OLD.sl_name IS NULL
    DO
INSERT INTO shoelaces (sh_id, sl_name)
    VALUES(NEW.sh_id, NEW.sl_name);

-- Testing: result should be a new record in 'shoelaces'
UPDATE footwear SET sl_name = 'sl3' WHERE sh_name = 'sh2';

-- but that doesn't happen:
SELECT * FROM shoelaces;

 sl_id | sh_id | sl_name
-------+-------+---------
     1 |     1 | sl1
     2 |     3 | sl2
(2 rows)
---<--------------------cut here---------------end--------------------->---

Any tips would be much appreciated.

--
Seb

Re: conditional rule not applied

From
Seb
Date:
On Wed, 30 Dec 2009 19:39:15 -0600,
Seb <spluque@gmail.com> wrote:

> CREATE RULE footwear_nothing_upd AS
>     ON UPDATE TO footwear DO INSTEAD NOTHING;
> CREATE RULE footwear_newshoelaces_upd AS
>     ON UPDATE TO footwear
>     WHERE NEW.sl_name <> OLD.sl_name AND OLD.sl_name IS NULL
>     DO
> INSERT INTO shoelaces (sh_id, sl_name)
>     VALUES(NEW.sh_id, NEW.sl_name);

I think my error is in the test expression, which doesn't deal properly
with the null value, so correcting:

CREATE RULE footwear_nothing_upd AS
    ON UPDATE TO footwear DO INSTEAD NOTHING;
CREATE RULE footwear_newshoelaces_upd AS
    ON UPDATE TO footwear
    WHERE NEW.sl_name IS DISTINCT FROM OLD.sl_name AND OLD.sl_name IS NULL
    DO
INSERT INTO shoelaces (sh_id, sl_name)
    VALUES(NEW.sh_id, NEW.sl_name);

However, could a more direct and robust test for an inexistent record in
'shoelaces' be made?


--
Seb

Re: conditional rule not applied

From
Seb
Date:
On Wed, 30 Dec 2009 20:04:51 -0600,
Seb <spluque@gmail.com> wrote:

> On Wed, 30 Dec 2009 19:39:15 -0600,
> Seb <spluque@gmail.com> wrote:

> CREATE RULE footwear_nothing_upd AS
>> ON UPDATE TO footwear DO INSTEAD NOTHING; CREATE RULE
>> footwear_newshoelaces_upd AS ON UPDATE TO footwear WHERE NEW.sl_name
>> <> OLD.sl_name AND OLD.sl_name IS NULL DO INSERT INTO shoelaces
>> (sh_id, sl_name) VALUES(NEW.sh_id, NEW.sl_name);

> I think my error is in the test expression, which doesn't deal
> properly with the null value, so correcting:

> CREATE RULE footwear_nothing_upd AS
>     ON UPDATE TO footwear DO INSTEAD NOTHING;
> CREATE RULE footwear_newshoelaces_upd AS
>     ON UPDATE TO footwear
>     WHERE NEW.sl_name IS DISTINCT FROM OLD.sl_name AND OLD.sl_name IS NULL
>     DO
> INSERT INTO shoelaces (sh_id, sl_name)
>     VALUES(NEW.sh_id, NEW.sl_name);

> However, could a more direct and robust test for an inexistent record
> in 'shoelaces' be made?

Any ideas? I'm not sure this is the best way to test whether the record
to update corresponds to a inexistent record in 'shoelaces'. Thanks.


--
Seb

Re: conditional rule not applied

From
Seb
Date:
On Tue, 05 Jan 2010 20:20:13 -0600,
Seb <spluque@gmail.com> wrote:

> On Wed, 30 Dec 2009 20:04:51 -0600,
> Seb <spluque@gmail.com> wrote:

> On Wed, 30 Dec 2009 19:39:15 -0600,
>> Seb <spluque@gmail.com> wrote:

> CREATE RULE footwear_nothing_upd AS
>>> ON UPDATE TO footwear DO INSTEAD NOTHING; CREATE RULE
>>> footwear_newshoelaces_upd AS ON UPDATE TO footwear WHERE NEW.sl_name
>>> <> OLD.sl_name AND OLD.sl_name IS NULL DO INSERT INTO shoelaces
>>> (sh_id, sl_name) VALUES(NEW.sh_id, NEW.sl_name);

>> I think my error is in the test expression, which doesn't deal
>> properly with the null value, so correcting:

>> CREATE RULE footwear_nothing_upd AS
>>     ON UPDATE TO footwear DO INSTEAD NOTHING;
>> CREATE RULE footwear_newshoelaces_upd AS
>>     ON UPDATE TO footwear
>>     WHERE NEW.sl_name IS DISTINCT FROM OLD.sl_name AND OLD.sl_name IS NULL
>>     DO
>> INSERT INTO shoelaces (sh_id, sl_name)
>>     VALUES(NEW.sh_id, NEW.sl_name);

>> However, could a more direct and robust test for an inexistent record
>> in 'shoelaces' be made?

> Any ideas? I'm not sure this is the best way to test whether the
> record to update corresponds to a inexistent record in
> 'shoelaces'. Thanks.

Would this express the intention any better?

CREATE RULE footwear_nothing_upd AS
    ON UPDATE TO footwear DO INSTEAD NOTHING;
CREATE RULE footwear_newshoelaces_upd AS
    ON UPDATE TO footwear
    WHERE NOT EXISTS (SELECT sh_id FROM shoelaces WHERE NEW.sh_id=shoelaces.sh_id)
    DO
INSERT INTO shoelaces (sh_id, sl_name)
    VALUES(NEW.sh_id, NEW.sl_name);


--
Seb

Re: conditional rule not applied

From
Seb
Date:
On Wed, 06 Jan 2010 09:39:45 -0600,
Seb <spluque@gmail.com> wrote:

> Would this express the intention any better?

> CREATE RULE footwear_nothing_upd AS
>     ON UPDATE TO footwear DO INSTEAD NOTHING;
> CREATE RULE footwear_newshoelaces_upd AS
>     ON UPDATE TO footwear
>     WHERE NOT EXISTS (SELECT sh_id FROM shoelaces WHERE NEW.sh_id=shoelaces.sh_id)
>     DO
> INSERT INTO shoelaces (sh_id, sl_name)
>     VALUES(NEW.sh_id, NEW.sl_name);

Adding to my confusion here, is the fact that the rule above seems to
work well, even though the docs say:

---<--------------------cut here---------------start------------------->---
condition

 Any SQL conditional expression (returning boolean). The condition
 expression cannot refer to any tables except NEW and OLD, and cannot
 contain aggregate functions.
---<--------------------cut here---------------end--------------------->---

So the WHERE condition in the rule above should not be allowed since it
does reference a table other than NEW and OLD in the EXISTS statement.
Any enlightening comments appreciated.


--
Seb

Re: conditional rule not applied

From
Scott Marlowe
Date:
On Wed, Dec 30, 2009 at 6:39 PM, Seb <spluque@gmail.com> wrote:
> CREATE RULE footwear_nothing_upd AS
>    ON UPDATE TO footwear DO INSTEAD NOTHING;
> CREATE RULE footwear_newshoelaces_upd AS
>    ON UPDATE TO footwear
>    WHERE NEW.sl_name <> OLD.sl_name AND OLD.sl_name IS NULL
>    DO
> INSERT INTO shoelaces (sh_id, sl_name)
>    VALUES(NEW.sh_id, NEW.sl_name);

Isn't that first rule gonna always fire and make the second one a NOOP?

Re: conditional rule not applied

From
Seb
Date:
On Thu, 7 Jan 2010 21:04:45 -0700,
Scott Marlowe <scott.marlowe@gmail.com> wrote:

> On Wed, Dec 30, 2009 at 6:39 PM, Seb <spluque@gmail.com> wrote:
>> CREATE RULE footwear_nothing_upd AS    ON UPDATE TO footwear DO
>> INSTEAD NOTHING; CREATE RULE footwear_newshoelaces_upd AS    ON
>> UPDATE TO footwear    WHERE NEW.sl_name <> OLD.sl_name AND
>> OLD.sl_name IS NULL    DO INSERT INTO shoelaces (sh_id, sl_name)  
>>  VALUES(NEW.sh_id, NEW.sl_name);

> Isn't that first rule gonna always fire and make the second one a
> NOOP?

No, the second is an implied ALSO, so it gets added to the DO INSTEAD
NOTHING.  This is actually the approach recommended in the man page for
CREATE RULE where the reasons for doing that are described.  The problem
with this is that it always displays the message "UPDATE 0" when in fact
the second rule may have also been applied with the INSERT.  I posted
this question to the postgresql.sql NG, where some discussion ensued.


--
Seb