Thread: Damn bug!

Damn bug!

From
Bernie Huang
Date:
Hi,

Sorry for the subject, but it is driving me crazy that I spend my entire
morning searching for this little bug.  =(

Well, I found it.

I have a field using array (eg; col1 text[]) in Postgres, and it's a
list of attributes.  (eg; {"hi","hello","whatever","Empty",...})

When I tried to update elements in the array via PHP script,

$query = "update table
          set col1[1]='$var1',
              col1[2]='$var2',
              ...
              col1[4]='$var4'";

it worked alright; however, when it came to the word 'Empty', it just
wouldn't update. So, after a lot of struggle... )xp ... I finally
replaced the word 'Empty' with 'None' or something alike, and it worked!

Although this is a rare case, I thought I would just share this with you
so you won't get tripped over it.  Don't know if it's a PHP bug or a
Postgres bug!


- Bernie

Attachment

Re: Damn bug!

From
Andrew McMillan
Date:
Bernie Huang wrote:
>
> I have a field using array (eg; col1 text[]) in Postgres, and it's a
> list of attributes.  (eg; {"hi","hello","whatever","Empty",...})
>
> When I tried to update elements in the array via PHP script,
>
> $query = "update table
>           set col1[1]='$var1',
>               col1[2]='$var2',
>               ...
>               col1[4]='$var4'";
>
> it worked alright; however, when it came to the word 'Empty', it just
> wouldn't update. So, after a lot of struggle... )xp ... I finally
> replaced the word 'Empty' with 'None' or something alike, and it worked!
>

I think that there is something strange going on with assignment of
multiple array subscripts in PostgreSQL there...

It looks like a PostgreSQL limitation which is being detected on an
'insert' but is not being detected on an 'update' (and it probably
should be).

Look at the interesting log of various stuff from psql doing similar
things:

testing=# create table t1 ( c1 text[] );
CREATE
testing=# insert into t1 (c1[1], c1[2], c1[3] ) values('not',
'actually', 'empty' );
ERROR:  Attribute 'c1' specified more than once
testing=# insert into t1 (c1[1], c1[2], c1[3] ) values('not',
'actually', 'empt' );
ERROR:  Attribute 'c1' specified more than once
testing=# insert into t1 (c1 ) values('{"not", "actually", "empt"}' );
INSERT 373577 1
testing=# select * from t1;
            c1
---------------------------
 {"not","actually","empt"}
(1 row)

testing=# insert into t1 (c1 ) values('{"not", "actually", "empty"}' );
INSERT 373578 1
testing=# select * from t1;
             c1
----------------------------
 {"not","actually","empt"}
 {"not","actually","empty"}
(2 rows)

testing=# update t1 set c1[2] = 'empty';
UPDATE 2
testing=# select * from t1;
           c1
-------------------------
 {"not","empty","empt"}
 {"not","empty","empty"}
(2 rows)

testing=# update t1 set c1[2] = 'empty', c1[3] = 'full';
UPDATE 2
testing=# select * from t1;
           c1
-------------------------
 {"not","empty","full"}
 {"not","empty","empty"}
(2 rows)

----------------------------------------------------
Now that was a bit strange, wasn't it?
----------------------------------------------------



testing=# update t1 set c1[2] = 'other', c1[3] = 'full';
UPDATE 2
testing=# select * from t1;
           c1
-------------------------
 {"not","other","full"}
 {"not","other","empty"}
(2 rows)

testing=# update t1 set c1[3] = 'full';
UPDATE 2
testing=# select * from t1;
           c1
------------------------
 {"not","other","full"}
 {"not","other","full"}
(2 rows)

testing=# update t1 set c1[2] = 'strange', c1[3] = 'notfull';
UPDATE 2
testing=# select * from t1;
            c1
--------------------------
 {"not","strange","full"}
 {"not","strange","full"}
(2 rows)


----------------------------------------------------------
So it looks like UPDATE is silently ignoring second and subsequent
references to the same array variable.  In most cases...

A good workaround muight be for you to use the '{"blah", "blah",
"blah"}' syntax for updating the array, although it's a pretty messy
syntax.

Cheers,
                    Andrew.
--
_____________________________________________________________________
            Andrew McMillan, e-mail: Andrew@cat-it.co.nz
Catalyst IT Ltd, PO Box 10-225, Level 22, 105 The Terrace, Wellington
Me: +64 (21) 635 694, Fax: +64 (4) 499 5596, Office: +64 (4) 499 2267

Re: Damn bug!

From
JanWieck@t-online.de (Jan Wieck)
Date:
Bernie Huang wrote:
[Charset iso-8859-15 unsupported, filtering to ASCII...]
> Hi,
>
> Sorry for the subject, but it is driving me crazy that I spend my entire
> morning searching for this little bug.  =(
>
> Well, I found it.
>
> I have a field using array (eg; col1 text[]) in Postgres, and it's a
> list of attributes.  (eg; {"hi","hello","whatever","Empty",...})
>
> When I tried to update elements in the array via PHP script,
>
> $query = "update table
>           set col1[1]='$var1',
>               col1[2]='$var2',
>               ...
>               col1[4]='$var4'";
>
> it worked alright; however, when it came to the word 'Empty', it just
> wouldn't update. So, after a lot of struggle... )xp ... I finally
> replaced the word 'Empty' with 'None' or something alike, and it worked!
>
> Although this is a rare case, I thought I would just share this with you
> so you won't get tripped over it.  Don't know if it's a PHP bug or a
> Postgres bug!

    A Postgres bug:

        pgsql=# create table t1 (a text[]);
        CREATE
        pgsql=# insert into t1 values ('{"foo","bar"}');
        INSERT 18872 1
        pgsql=# select * from t1;
               a
        ---------------
         {"foo","bar"}
        (1 row)

        pgsql=# update t1 set a[1] = 'new', a[2] = 'Empty';
        UPDATE 1
        pgsql=# select * from t1;
               a
        ---------------
         {"new","bar"}
        (1 row)

        pgsql=# update t1 set a[1] = 'next', a[2] = 'None';
        UPDATE 1
        pgsql=# select * from t1;
                a
        -----------------
         {"next","None"}
        (1 row)

    Don't know where this might happen, but it's interesting that
    our ORDBMS treats some "text" literal special. Looks  like  a
    fogotten hack somewhere :-)


Jan

--

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me.                                  #
#================================================== JanWieck@Yahoo.com #

Re: Damn bug!

From
Tom Lane
Date:
JanWieck@t-online.de (Jan Wieck) writes:
>         pgsql=# update t1 set a[1] = 'new', a[2] = 'Empty';
>         UPDATE 1
>         pgsql=# select * from t1;
>                a
>         ---------------
>          {"new","bar"}
>         (1 row)

>     Don't know where this might happen, but it's interesting that
>     our ORDBMS treats some "text" literal special.

It's not dependent on what the text is, but it *is* dependent on
the length of the text.

regression=# select * from t1;
       a
---------------
 {"foo","bar"}
(1 row)

regression=# update t1 set a[1] = 'foo1', a[2] = 'barge';
UPDATE 1
regression=# select * from t1;
       a
----------------
 {"foo1","bar"}
(1 row)

regression=# update t1 set a[1] = 'foo2', a[2] = 'barg';
UPDATE 1
regression=# select * from t1;
        a
-----------------
 {"foo2","barg"}
(1 row)


I was confused a few days ago when people claimed that it was possible
to update different elements of an array with separate assignments in
a single UPDATE.  From what I know of the system, this *should not
work* --- you should get only one of the updates applied, because
each assignment will independently invoke array_set and construct
a separate new value for what's really the same field.

I think what we're seeing here is evidence that it really doesn't work.
Possibly there's some hack in array_set that overwrites the source
data (which it shouldn't be doing, in any case!) when the data length
doesn't need to change.  Needs more digging to understand in detail...

            regards, tom lane

Re: Damn bug!

From
Tom Lane
Date:
I wrote:
> I think what we're seeing here is evidence that it really doesn't work.
> Possibly there's some hack in array_set that overwrites the source
> data (which it shouldn't be doing, in any case!) when the data length
> doesn't need to change.  Needs more digging to understand in detail...

Indeed, it looks like that's exactly what's happening.  Try this on
for size:

regression=# select * from t1;
       a
----------------
 {"foo1","bar"}
(1 row)

regression=# begin;
BEGIN
regression=# update t1 set a[1] = 'foot';
UPDATE 1
regression=# abort;
ROLLBACK
regression=# select * from t1;
       a
----------------
 {"foot","bar"}
(1 row)

<Dana Carvey> My, isn't *that* special ... </Dana Carvey>

Evidently, array_set scribbles on its source object when the size
of the object isn't changed by the element assignment.  This is why
it's possible for more than one element assignment to appear to work;
when the later assignments are executed with the same old source
datum, they see the other fields as updated.  Too bad scribbling on
disk buffers is verboten.

I have to hack on the array support soon for TOAST anyway, and I'll
make sure that this idiocy goes away then.  That will mean that
in fact you don't get more than one array update per UPDATE.  Don't
see any easy way around that, unless we want to hack on the parser
to convert

    UPDATE table SET a[1] = foo, a[2] = bar

into

    UPDATE table SET a = array_set(array_set(a, 1, foo), 2, bar)

That might not be totally impractical but it looks ugly... Thomas,
what do you think about it?

            regards, tom lane

Re: Damn bug!

From
Tom Lane
Date:
I believe I've fixed all these array problems as of current CVS sources,
a/k/a Postgres 7.1-to-be.

There's a lot more that could be done (eg, addressing the inability to
have individual NULL elements in an array), but other issues beckon...

            regards, tom lane