On Thu, Jan 25, 2018 at 2:33 PM, David G. Johnston <
david.g.johns...@gmail.com> wrote:

> On Thu, Jan 25, 2018 at 3:06 PM, Ian Harding <harding....@gmail.com>
> wrote:
>
>>
>>       4 |        3 | Top.Bar.Blah
>>       5 |        4 | Top.Bar.Blah.Scooby
>> ​​
>>
>
>
>> barf$#             UPDATE area SET areapath = (select areapath from area
>> a where areaid = area.parentid) || subpath(areapath, nlevel(areapath) - 1)
>> barf$#             WHERE OLD.areapath @> areapath;
>>
>
> barf=# update area set parentid = 2 where areaid = 4;
>>
>>
> OLD.areapath = Top.Bar.Blah
>
> ​When its Top.Bar.Blah.Scooby 's turn to be updated its parent is 4 which
> has a value of "Top.Bar.Blah" and so nothing happens.  It doesn't matter
> whether row 4 or row 5 occurs first - when multiple rows are updated your
> subselect presents the exact same data to each row and it doesn't take into
> account updates applied to other rows made concurrently.
>
> ​Top.Bar.Blah 's parent was changed to 2 so it does go from "Top.Bar.Blah"
> to "Top.Foo.Blah"
>
> When you then run your update manually row 5 sees the newly committed
> areapath value for row 4 and now affects the change to
> "Top.Foo.Blah.Scooby"​
>
> IOW, cascading updates are not possible (regardless of whether you are
> doing them in a trigger or manually).  If you had a three-deep parent-child
> hierarchy to change in your example you would have seen that your example
> script would only have corrected the first two levels.
>
> Probably your trigger will need to capture (SELECT areapath ​.. WHERE id =
> NEW.parentid) and then use that constant in your SET clause.
>
> ​David J.
> ​
>
> For the record:

CREATE OR REPLACE FUNCTION trig_areapath_u() RETURNS trigger AS
$$
DECLARE
  ltree_parentpath ltree;
  ltree_mypath ltree;
  int_cnt int;
BEGIN
  IF TG_OP = 'UPDATE' THEN
      IF (COALESCE(OLD.parentid,0) != COALESCE(NEW.parentid,0)) THEN
            -- Get the new parent path and save it
            -- Get the old path for this item and save it
            -- Replace the first X elements of the path for this and all
            -- my children with the parent path

            SELECT areapath
                INTO ltree_parentpath
                FROM area
                WHERE areaid = NEW.parentid;

            ltree_mypath := OLD.areapath;

            UPDATE area SET areapath = ltree_parentpath ||
                subpath(areapath, nlevel(ltree_parentpath) )
            WHERE ltree_mypath @> areapath;

            GET DIAGNOSTICS int_cnt = ROW_COUNT;
            RAISE NOTICE 'Rows affected: %', int_cnt;
      END IF;
  END IF;

  RETURN NULL;
END
$$
LANGUAGE 'plpgsql' VOLATILE;

Reply via email to