Обсуждение: Help with array constraints

Поиск
Список
Период
Сортировка

Help with array constraints

От
Jason Hihn
Дата:
Two tables (simplified):

CREATE TABLE _test (
    id CHAR(1),
    PRIMARY KEY(id)
);

INSERT INTO _test VALUES ('a');
INSERT INTO _test VALUES ('b');

CREATE TABLE test (
    letter CHAR(1)[3] NOT NULL REFERENCES _test(id)
    PRIMARY KEY(letter)
);

CREATE TABLE / PRIMARY KEY will create implicit index 'test_pkey' for table
'test'
NOTICE:  CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
check(s)
ERROR:  Unable to identify an operator '=' for types 'character[]' and
'character'
        You will have to retype this query using an explicit cast

Can someone please explain that in English? I want ALL the letter field
values to be checked against what is in the _test table id field when a row
is inserted. For example, 'a' and 'b' is in the _test table now, if I insert
an 'a' or 'b' into test, it will suceed. If I insert a 'c' or 'd' it should
fail.

What must I do?

I am on v7.2.

Thank you.


Re: Help with array constraints

От
Antti Haapala
Дата:
On Thu, 3 Apr 2003, Jason Hihn wrote:

> Two tables (simplified):
>
> CREATE TABLE _test (
>     id CHAR(1),
>     PRIMARY KEY(id)
> );
>
> INSERT INTO _test VALUES ('a');
> INSERT INTO _test VALUES ('b');
>
> CREATE TABLE test (
>     letter CHAR(1)[3] NOT NULL REFERENCES _test(id)
>     PRIMARY KEY(letter)
> );
>
> CREATE TABLE / PRIMARY KEY will create implicit index 'test_pkey' for table
> 'test'
> NOTICE:  CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
> check(s)
> ERROR:  Unable to identify an operator '=' for types 'character[]' and
> 'character'
>         You will have to retype this query using an explicit cast
>
> Can someone please explain that in English?

There's no way to compare character string (char(1)) and character string
array (char(1)[3]) with '=' operator. PostgreSQL uses triggers to
implement foreign key constraints. Trigger creation fails because the type
of _test.id doesn't match that of test.letter.

> I want ALL the letter field values to be checked against what is in the
> _test table id field when a row is inserted. For example, 'a' and 'b' is
> in the _test table now, if I insert an 'a' or 'b' into test, it will
> suceed. If I insert a 'c' or 'd' it should fail.

Split your fixed sized array to separate table columns and make them
foreign keys and it will work.

And probably you don't want to use char(1), because it uses at least 5
bytes of storage for each single character.

--
Antti Haapala


Re: Help with array constraints

От
Richard Huxton
Дата:
On Thursday 03 Apr 2003 3:48 pm, Jason Hihn wrote:
> Two tables (simplified):
>
> CREATE TABLE _test (
>     id CHAR(1),
>     PRIMARY KEY(id)
> );
>
> INSERT INTO _test VALUES ('a');
> INSERT INTO _test VALUES ('b');
>
> CREATE TABLE test (
>     letter CHAR(1)[3] NOT NULL REFERENCES _test(id)
>     PRIMARY KEY(letter)
> );
>
> CREATE TABLE / PRIMARY KEY will create implicit index 'test_pkey' for table
> 'test'
> NOTICE:  CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
> check(s)
> ERROR:  Unable to identify an operator '=' for types 'character[]' and
> 'character'
>         You will have to retype this query using an explicit cast
>
> Can someone please explain that in English?

You've asked it to compare the whole array of single-chars with a single-char.
PG says it can't do that.

> I want ALL the letter field
> values to be checked against what is in the _test table id field when a row
> is inserted. For example, 'a' and 'b' is in the _test table now, if I
> insert an 'a' or 'b' into test, it will suceed. If I insert a 'c' or 'd' it
> should fail.
>
> What must I do?

You might be able to add separate FK constraints (see CREATE TABLE or ALTER
TABLE) for each array element.

CREATE TABLE test (
...
CONSTRAINT first_letter FOREIGN KEY (letter[0]) REFERENCES _test(id),
CONSTRAINT first_letter FOREIGN KEY (letter[1]) REFERENCES _test(id),
CONSTRAINT first_letter FOREIGN KEY (letter[2]) REFERENCES _test(id)
)

Don't know if that will work - I'm wary of using arrays myself. I'm a bit
surprised that you can create a primary key on an array.

The other option is that you might need to alter your design. Either three
separate fields or a separate table with (letter_index, char_value) so you
only need the FK constraint on "char_value", and letter_index can be
constrained with a CHECK.

--
  Richard Huxton


Re: Help with array constraints

От
Jason Hihn
Дата:
> >
> > What must I do?
>
> You might be able to add separate FK constraints (see CREATE
> TABLE or ALTER
> TABLE) for each array element.
>
> CREATE TABLE test (
> ...
> CONSTRAINT first_letter FOREIGN KEY (letter[0]) REFERENCES _test(id),
> CONSTRAINT first_letter FOREIGN KEY (letter[1]) REFERENCES _test(id),
> CONSTRAINT first_letter FOREIGN KEY (letter[2]) REFERENCES _test(id)
> )
>
> Don't know if that will work - I'm wary of using arrays myself. I'm a bit
> surprised that you can create a primary key on an array.

Whoops, that's not actually in my application!

> The other option is that you might need to alter your design.
> Either three
> separate fields or a separate table with (letter_index,
> char_value) so you
> only need the FK constraint on "char_value", and letter_index can be
> constrained with a CHECK.

Design altering is a BAD thing because I am attempting a port from a DBMS
that supports arrays, and they are used quite extensively in places.
Adjusting the schema would be to take a large hit, and the powers that be
are already weary about doing the port in the first place. Seeing this was
now supported a big joy for me, because it makes it so much easier.


Re: Help with array constraints

От
Stephan Szabo
Дата:
On Thu, 3 Apr 2003, Jason Hihn wrote:

> Two tables (simplified):
>
> CREATE TABLE _test (
>     id CHAR(1),
>     PRIMARY KEY(id)
> );
>
> INSERT INTO _test VALUES ('a');
> INSERT INTO _test VALUES ('b');
>
> CREATE TABLE test (
>     letter CHAR(1)[3] NOT NULL REFERENCES _test(id)
>     PRIMARY KEY(letter)
> );
>
> CREATE TABLE / PRIMARY KEY will create implicit index 'test_pkey' for table
> 'test'
> NOTICE:  CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
> check(s)
> ERROR:  Unable to identify an operator '=' for types 'character[]' and
> 'character'
>         You will have to retype this query using an explicit cast
>
> Can someone please explain that in English? I want ALL the letter field
> values to be checked against what is in the _test table id field when a row
> is inserted. For example, 'a' and 'b' is in the _test table now, if I insert
> an 'a' or 'b' into test, it will suceed. If I insert a 'c' or 'd' it should
> fail.
>
> What must I do?

Probably write specialized triggers. As a note, if test is even marginally
large, the check for update/delete on _test is going to probably be
immensely painful unless you have an index on the individual elements of
the array rather than the array as a whole (AFAIK that'll index the
complete array, which means that it's probably not terribly useful for
searching for subelements).

If you only want to do insert/update on test time checks (and not worry
about update/delete from _test) this becomes somewhat easier.  You can
make a function to do the checks against _test for each element value and
use it in a check constraint.


Re: Help with array constraints

От
"Nigel J. Andrews"
Дата:
On Thu, 3 Apr 2003, Jason Hihn wrote:

> > >
> > > What must I do?
> >
> > You might be able to add separate FK constraints (see CREATE
> > TABLE or ALTER
> > TABLE) for each array element.
> >
> > CREATE TABLE test (
> > ...
> > CONSTRAINT first_letter FOREIGN KEY (letter[0]) REFERENCES _test(id),
> > CONSTRAINT first_letter FOREIGN KEY (letter[1]) REFERENCES _test(id),
> > CONSTRAINT first_letter FOREIGN KEY (letter[2]) REFERENCES _test(id)
> > )
> >
> > Don't know if that will work - I'm wary of using arrays myself. I'm a bit
> > surprised that you can create a primary key on an array.
>
> Whoops, that's not actually in my application!
>
> > The other option is that you might need to alter your design.
> > Either three
> > separate fields or a separate table with (letter_index,
> > char_value) so you
> > only need the FK constraint on "char_value", and letter_index can be
> > constrained with a CHECK.
>
> Design altering is a BAD thing because I am attempting a port from a DBMS
> that supports arrays, and they are used quite extensively in places.
> Adjusting the schema would be to take a large hit, and the powers that be
> are already weary about doing the port in the first place. Seeing this was
> now supported a big joy for me, because it makes it so much easier.


Well that constraint reply didn't contain what I was expecting to see after I
started reading it so...

You probably want to write your own function(s) and install it(them) as
trigger(s).

create function letter_fk () returns trigger as '
declare
    ind    integer;
begin
    for ind in array_dims(NEW.letters) loop
        perform 1 from _test where id = NEW.letters[ind];
        if not found then
            raise exception ''My foriegn key constraint
violation'';
        end if;
    end loop;

    return NEW;
end;
' as language 'plpgsql';

create trigger my_array_fkey
 before insert
 on test
 for each row execute procedure letter_fk();


Or something like that anyway. Also that's only a start.


--
Nigel J. Andrews


Re: Help with array constraints

От
Jason Hihn
Дата:
...
> There's no way to compare character string (char(1)) and character string
> array (char(1)[3]) with '=' operator. PostgreSQL uses triggers to
> implement foreign key constraints. Trigger creation fails because the type
> of _test.id doesn't match that of test.letter.
>
> > I want ALL the letter field values to be checked against what is in the
> > _test table id field when a row is inserted. For example, 'a' and 'b' is
> > in the _test table now, if I insert an 'a' or 'b' into test, it will
> > succeed. If I insert a 'c' or 'd' it should fail.
>
> Split your fixed sized array to separate table columns and make them
> foreign keys and it will work.

Not acceptable, or I would have done it. ;-) My alternative is to break it
out into tables, but since each record has several arrays per, that means
there's many, many little tables (with key data) that I'd have to make and
maintain.

> And probably you don't want to use char(1), because it uses at least 5
> bytes of storage for each single character.

Well then what should I use? "char" is an internal type, and it is not
specified if it is approved for use outside the internal uses...

Has no one written a generic trigger to do this? Also could be implemented
is trigger based array bounding if the bounds can be known to the trigger.


Re: Help with array constraints

От
Jason Hihn
Дата:
> -----Original Message-----
> From: pgsql-general-owner@postgresql.org
> [mailto:pgsql-general-owner@postgresql.org]On Behalf Of Nigel J. Andrews
> Sent: Thursday, April 03, 2003 11:19 AM
> To: Jason Hihn
> Cc: Richard Huxton; pgsql-general@postgresql.org
> Subject: Re: [GENERAL] Help with array constraints
>
>
> On Thu, 3 Apr 2003, Jason Hihn wrote:
> You probably want to write your own function(s) and install it(them) as
> trigger(s).
>
> create function letter_fk () returns trigger as '
> declare
>     ind    integer;
> begin
>     for ind in array_dims(NEW.letters) loop
>         perform 1 from _test where id = NEW.letters[ind];
>         if not found then
>             raise exception ''My foriegn key constraint
> violation'';
>         end if;
>     end loop;
>
>     return NEW;
> end;
> ' as language 'plpgsql';
>
> create trigger my_array_fkey
>  before insert
>  on test
>  for each row execute procedure letter_fk();
>
>
> Or something like that anyway. Also that's only a start.

Ah that's more like what I'm looking for. I have to attempt a select of the
new letter in the contrainted-to table. If it finds it, success! If the
contrainted-to table has an index, it should be very fast no matter how many
letters it's being constrained against...
"SELECT NEW.letter FROM constraint_table WHERE letter=id"
if null set raise exception.

I would have thought this would be built in? Can it be?


Re: Help with array constraints

От
Joe Conway
Дата:
Jason Hihn wrote:
> Ah that's more like what I'm looking for. I have to attempt a select of the
> new letter in the contrainted-to table. If it finds it, success! If the
> contrainted-to table has an index, it should be very fast no matter how many
> letters it's being constrained against...
> "SELECT NEW.letter FROM constraint_table WHERE letter=id"
> if null set raise exception.

I didn't follow this thread in its entirety, but it sounds like you need
an "array contains element x" function for use as a trigger? I think
there may be something that does that already in contrib/array or
contrib/intarray (I see you're not using int arrays, but maybe you could
port the C code).


> I would have thought this would be built in? Can it be?
>

I'm currently working on a fairly major upgrade to array support. No
promises on exactly what will make it into 7.4, but I'm planning to look
at contrib/array and contrib/intarray and rewrite or move as much of it
as makes sense into the backend (subject of course to acceptance of the
patches).

Joe